Merge branch 'mainstream'
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Fri, 19 Jul 2013 10:51:38 +0000 (12:51 +0200)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Fri, 19 Jul 2013 10:51:38 +0000 (12:51 +0200)
126 files changed:
AUTHORS
DESIGN
FAQ
INSTALL
Makefile.am
NEWS
configure.ac
datapath/datapath.c
datapath/datapath.h
datapath/flow.c
datapath/flow.h
datapath/linux/compat/gso.c
datapath/linux/compat/include/linux/netdevice.h
datapath/linux/compat/netdevice.c
datapath/vport-internal_dev.c
datapath/vport-netdev.c
include/sparse/pthread.h
lib/async-append-aio.c [new file with mode: 0644]
lib/async-append-sync.c [new file with mode: 0644]
lib/async-append.h [new file with mode: 0644]
lib/automake.mk
lib/bfd.c
lib/bfd.h
lib/bitmap.c
lib/bundle.c
lib/bundle.h
lib/byteq.c
lib/byteq.h
lib/cfm.c
lib/compiler.h
lib/daemon.c
lib/dpif-linux.c
lib/dpif-netdev.c
lib/dynamic-string.c
lib/dynamic-string.h
lib/fatal-signal.c
lib/hindex.c
lib/jsonrpc.c
lib/lacp.c
lib/latch.c [new file with mode: 0644]
lib/latch.h [new file with mode: 0644]
lib/learn.c
lib/learn.h
lib/learning-switch.c
lib/match.c
lib/meta-flow.c
lib/meta-flow.h
lib/multipath.c
lib/multipath.h
lib/netdev-linux.c
lib/netlink-socket.c
lib/netlink-socket.h
lib/netlink.c
lib/nx-match.c
lib/nx-match.h
lib/ofp-msgs.h
lib/ofp-parse.c
lib/ofp-parse.h
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.h
lib/packets.h
lib/poll-loop.c
lib/poll-loop.h
lib/rconn.c
lib/route-table.c
lib/stp.c
lib/stream-fd.c
lib/stress-unixctl.man [deleted file]
lib/stress.c [deleted file]
lib/stress.h [deleted file]
lib/timeval.c
lib/timeval.h
lib/util.c
lib/util.h
lib/vlog.c
lib/vlog.h
lib/worker.c [deleted file]
lib/worker.h [deleted file]
m4/openvswitch.m4
manpages.mk
ofproto/automake.mk
ofproto/ofproto-dpif-ipfix.c
ofproto/ofproto-dpif-mirror.c [new file with mode: 0644]
ofproto/ofproto-dpif-mirror.h [new file with mode: 0644]
ofproto/ofproto-dpif-xlate.c
ofproto/ofproto-dpif-xlate.h
ofproto/ofproto-dpif.c
ofproto/ofproto-dpif.h
ofproto/ofproto.c
ovsdb/ovsdb-server.1.in
ovsdb/ovsdb-server.c
rhel/openvswitch-fedora.spec.in
rhel/openvswitch.spec.in
tests/.gitignore
tests/learn.at
tests/library.at
tests/ofp-print.at
tests/ofproto-dpif.at
tests/ofproto-macros.at
tests/ofproto.at
tests/ovs-vsctl.at
tests/ovsdb-server.at
tests/test-bundle.c
tests/test-csum.c
tests/test-hindex.c
tests/test-multipath.c
tests/test-util.c
utilities/automake.mk
utilities/bugtool/automake.mk
utilities/bugtool/ovs-bugtool-bfd-show [new file with mode: 0755]
utilities/bugtool/ovs-bugtool-list-dbs [new file with mode: 0755]
utilities/bugtool/plugins/network-status/openvswitch.xml
utilities/ovs-controller.c
utilities/ovs-ctl.in
utilities/ovs-dev.py [new file with mode: 0755]
utilities/ovs-ofctl.c
utilities/ovs-vsctl.c
vswitchd/bridge.c
vswitchd/ovs-vswitchd.8.in
vswitchd/ovs-vswitchd.c
vswitchd/system-stats.c
vswitchd/vswitch.ovsschema
vswitchd/vswitch.xml
xenserver/README
xenserver/openvswitch-xen.spec.in

diff --git a/AUTHORS b/AUTHORS
index 26e1f7f..c2ef06c 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -56,6 +56,7 @@ Keith Amidon            keith@nicira.com
 Krishna Kondaka         kkondaka@vmware.com
 Kyle Mestery            kmestery@cisco.com
 Leo Alterman            lalterman@nicira.com
+Linda Sun               lsun@vmware.com
 Lorand Jakab            lojakab@cisco.com
 Luca Giraudo            lgiraudo@nicira.com
 Martin Casado           casado@nicira.com
@@ -95,6 +96,7 @@ Vivien Bernet-Rollande  vbr@soprive.net
 Wei Yongjun             yjwei@cn.fujitsu.com
 Yasuhito Takamiya       yasuhito@gmail.com
 Yu Zhiguo               yuzg@cn.fujitsu.com
+ZhengLingyun            konghuarukhr@163.com
 Zoltan Kiss             zoltan.kiss@citrix.com
 Zhi Yong Wu             zwu.kernel@gmail.com
 Zang MingJie            zealot0630@gmail.com
diff --git a/DESIGN b/DESIGN
index f3e9309..d36b025 100644 (file)
--- a/DESIGN
+++ b/DESIGN
@@ -84,8 +84,8 @@ OFPP_LOCAL as a physical port and support OFPAT_ENQUEUE on it as well.
 OFPT_FLOW_MOD
 =============
 
-The OpenFlow 1.0 specification for the behavior of OFPT_FLOW_MOD is
-confusing.  The following table summarizes the Open vSwitch
+The OpenFlow specification for the behavior of OFPT_FLOW_MOD is
+confusing.  The following tables summarize the Open vSwitch
 implementation of its behavior in the following categories:
 
     - "match on priority": Whether the flow_mod acts only on flows
@@ -93,7 +93,12 @@ implementation of its behavior in the following categories:
 
     - "match on out_port": Whether the flow_mod acts only on flows
       that output to the out_port included in the flow_mod message (if
-      out_port is not OFPP_NONE).
+      out_port is not OFPP_NONE).  OpenFlow 1.1 and later have a
+      similar feature (not listed separately here) for out_group.
+
+    - "match on flow_cookie": Whether the flow_mod acts only on flows
+      whose flow_cookie matches an optional controller-specified value
+      and mask.
 
     - "updates flow_cookie": Whether the flow_mod changes the
       flow_cookie of the flow or flows that it matches to the
@@ -120,6 +125,11 @@ implementation of its behavior in the following categories:
     - "zeros counters": Whether the flow_mod resets per-flow packet
       and byte counters to zero.
 
+    - "may add a new flow": Whether the flow_mod may add a new flow to
+      the flow table.  (Obviously this is always true for "add"
+      commands but in some OpenFlow versions "modify" and
+      "modify-strict" can also add new flows.)
+
     - "sends flow_removed message": Whether the flow_mod generates a
       flow_removed message for the flow or flows that it affects.
 
@@ -128,11 +138,17 @@ indicated behavior, "---" means that it does not, an empty cell means
 that the property is not applicable, and other values are explained
 below the table.
 
+OpenFlow 1.0
+------------
+
                                           MODIFY          DELETE
                              ADD  MODIFY  STRICT  DELETE  STRICT
                              ===  ======  ======  ======  ======
-match on priority            ---    ---     yes     ---     yes
+match on priority            yes    ---     yes     ---     yes
 match on out_port            ---    ---     ---     yes     yes
+match on flow_cookie         ---    ---     ---     ---     ---
+match on table_id            ---    ---     ---     ---     ---
+controller chooses table_id  ---    ---     ---
 updates flow_cookie          yes    yes     yes
 updates OFPFF_SEND_FLOW_REM  yes     +       +
 honors OFPFF_CHECK_OVERLAP   yes     +       +
@@ -141,6 +157,48 @@ updates hard_timeout         yes     +       +
 resets idle timer            yes     +       +
 resets hard timer            yes    yes     yes
 zeros counters               yes     +       +
+may add a new flow           yes    yes     yes
+sends flow_removed message   ---    ---     ---      %       %
+
+(+) "modify" and "modify-strict" only take these actions when they
+    create a new flow, not when they update an existing flow.
+
+(%) "delete" and "delete_strict" generates a flow_removed message if
+    the deleted flow or flows have the OFPFF_SEND_FLOW_REM flag set.
+    (Each controller can separately control whether it wants to
+    receive the generated messages.)
+
+OpenFlow 1.1
+------------
+
+OpenFlow 1.1 makes these changes:
+
+    - The controller now must specify the table_id of the flow match
+      searched and into which a flow may be inserted.  Behavior for a
+      table_id of 255 is undefined.
+
+    - A flow_mod, except an "add", can now match on the flow_cookie.
+
+    - When a flow_mod matches on the flow_cookie, "modify" and
+      "modify-strict" never insert a new flow.
+
+                                          MODIFY          DELETE
+                             ADD  MODIFY  STRICT  DELETE  STRICT
+                             ===  ======  ======  ======  ======
+match on priority            yes    ---     yes     ---     yes
+match on out_port            ---    ---     ---     yes     yes
+match on flow_cookie         ---    yes     yes     yes     yes
+match on table_id            yes    yes     yes     yes     yes
+controller chooses table_id  yes    yes     yes
+updates flow_cookie          yes    ---     ---
+updates OFPFF_SEND_FLOW_REM  yes     +       +
+honors OFPFF_CHECK_OVERLAP   yes     +       +
+updates idle_timeout         yes     +       +
+updates hard_timeout         yes     +       +
+resets idle timer            yes     +       +
+resets hard timer            yes    yes     yes
+zeros counters               yes     +       +
+may add a new flow           yes     #       #
 sends flow_removed message   ---    ---     ---      %       %
 
 (+) "modify" and "modify-strict" only take these actions when they
@@ -151,6 +209,62 @@ sends flow_removed message   ---    ---     ---      %       %
     (Each controller can separately control whether it wants to
     receive the generated messages.)
 
+(#) "modify" and "modify-strict" only add a new flow if the flow_mod
+    does not match on any bits of the flow cookie
+
+OpenFlow 1.2
+------------
+
+OpenFlow 1.2 makes these changes:
+
+    - Only "add" commands ever add flows, "modify" and "modify-strict"
+      never do.
+
+    - A new flag OFPFF_RESET_COUNTS now controls whether "modify" and
+      "modify-strict" reset counters, whereas previously they never
+      reset counters (except when they inserted a new flow).
+
+                                          MODIFY          DELETE
+                             ADD  MODIFY  STRICT  DELETE  STRICT
+                             ===  ======  ======  ======  ======
+match on priority            yes    ---     yes     ---     yes
+match on out_port            ---    ---     ---     yes     yes
+match on flow_cookie         ---    yes     yes     yes     yes
+match on table_id            yes    yes     yes     yes     yes
+controller chooses table_id  yes    yes     yes
+updates flow_cookie          yes    ---     ---
+updates OFPFF_SEND_FLOW_REM  yes    ---     ---
+honors OFPFF_CHECK_OVERLAP   yes    ---     ---
+updates idle_timeout         yes    ---     ---
+updates hard_timeout         yes    ---     ---
+resets idle timer            yes    ---     ---
+resets hard timer            yes    yes     yes
+zeros counters               yes     &       &
+may add a new flow           yes    ---     ---
+sends flow_removed message   ---    ---     ---      %       %
+
+(%) "delete" and "delete_strict" generates a flow_removed message if
+    the deleted flow or flows have the OFPFF_SEND_FLOW_REM flag set.
+    (Each controller can separately control whether it wants to
+    receive the generated messages.)
+
+(&) "modify" and "modify-strict" reset counters if the
+    OFPFF_RESET_COUNTS flag is specified.
+
+OpenFlow 1.3
+------------
+
+OpenFlow 1.3 makes these changes:
+
+    - Behavior for a table_id of 255 is now defined, for "delete" and
+      "delete-strict" commands, as meaning to delete from all tables.
+      A table_id of 255 is now explicitly invalid for other commands.
+
+    - New flags OFPFF_NO_PKT_COUNTS and OFPFF_NO_BYT_COUNTS for "add"
+      operations.
+
+The table for 1.3 is the same as the one shown above for 1.2.
+
 
 VLAN Matching
 =============
@@ -185,7 +299,7 @@ Each column is interpreted as follows.
 
     - OF1.0 and OF1.1: wwww/x,yy/z means dl_vlan wwww, OFPFW_DL_VLAN
       x, dl_vlan_pcp yy, and OFPFW_DL_VLAN_PCP z.  ? means that the
-      given nibble is ignored (and conventionally 0 for wwww or z,
+      given nibble is ignored (and conventionally 0 for wwww or yy,
       conventionally 1 for x or z).  <none> means that the given match
       is not supported.
 
diff --git a/FAQ b/FAQ
index 7df4e90..6b5d8da 100644 (file)
--- a/FAQ
+++ b/FAQ
@@ -146,6 +146,9 @@ A: The following table lists the Linux kernel versions against which the
        1.7.x      2.6.18 to 3.3
        1.8.x      2.6.18 to 3.4
        1.9.x      2.6.18 to 3.8
+       1.10.x     2.6.18 to 3.8
+       1.11.x     2.6.18 to 3.8
+       1.12.x     2.6.18 to 3.8
 
    Open vSwitch userspace should also work with the Linux kernel module
    built into Linux 3.3 and later.
@@ -934,22 +937,26 @@ A: Open vSwitch 1.9 and earlier support only OpenFlow 1.0 (plus
    extensions that bring in many of the features from later versions
    of OpenFlow).
 
-   Open vSwitch versions 1.10 and later will have experimental support
-   for OpenFlow 1.2 and 1.3.  On these versions of Open vSwitch, the
-   following command enables OpenFlow 1.0, 1.2, and 1.3 on bridge br0:
+   Open vSwitch 1.10 and later have experimental support for OpenFlow
+   1.2 and 1.3.  On these versions of Open vSwitch, the following
+   command enables OpenFlow 1.0, 1.2, and 1.3 on bridge br0:
 
        ovs-vsctl set bridge br0 protocols=OpenFlow10,OpenFlow12,OpenFlow13
 
+   Open vSwitch version 1.12 and later will have experimental support
+   for OpenFlow 1.1, 1.2, and 1.3.  On these versions of Open vSwitch,
+   the following command enables OpenFlow 1.0, 1.1, 1.2, and 1.3 on
+   bridge br0:
+
+       ovs-vsctl set bridge br0 protocols=OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13
+
    Use the -O option to enable support for later versions of OpenFlow
    in ovs-ofctl.  For example:
 
        ovs-ofctl -O OpenFlow13 dump-flows br0
 
-   Support for OpenFlow 1.1 is incomplete enough that it cannot yet be
-   enabled, even experimentally.
-
-   Support for OpenFlow 1.2 and 1.3 is still incomplete.  Work to be
-   done is tracked in OPENFLOW-1.1+ in the Open vSwitch source tree
+   Support for OpenFlow 1.1, 1.2, and 1.3 is still incomplete.  Work
+   to be done is tracked in OPENFLOW-1.1+ in the Open vSwitch sources
    (also via http://openvswitch.org/development/openflow-1-x-plan/).
    When support for a given OpenFlow version is solidly implemented,
    Open vSwitch will enable that version by default.
diff --git a/INSTALL b/INSTALL
index 0ff237c..5589fe7 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -107,6 +107,8 @@ installing the following to obtain better warnings:
 
     - GNU make.
 
+Also, you may find the ovs-dev script found in utilities/ovs-dev.py useful.
+
 Installation Requirements
 -------------------------
 
index 4c58b93..32e85d1 100644 (file)
@@ -203,7 +203,7 @@ ALL_LOCAL += check-assert-h-usage
 check-assert-h-usage:
        @if test -e $(srcdir)/.git && (git --version) >/dev/null 2>&1 && \
            (cd $(srcdir) && git --no-pager grep -l -E '[<]assert.h[>]') | \
-           $(EGREP) -v '^lib/(sflow_receiver|vlog|worker).c$$|^tests/'; \
+           $(EGREP) -v '^lib/(sflow_receiver|vlog).c$$|^tests/'; \
          then \
            echo "Files listed above unexpectedly #include <""assert.h"">."; \
            echo "Please use ovs_assert (from util.h) instead of assert."; \
diff --git a/NEWS b/NEWS
index b07a651..b46fc43 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,15 +1,20 @@
 post-v1.11.0
 ---------------------
     - OpenFlow:
+      * Experimental support for OpenFlow 1.1 (in addition to 1.2 and
+        1.3, which had experimental support in 1.10).
       * New support for matching outer source and destination IP address
         of tunneled packets, for tunnel ports configured with the newly
        added "remote_ip=flow" and "local_ip=flow" options.
+    - The Interface table in the database has a new "ifindex" column to
+      report the interface's OS-assigned ifindex.
     - New "check-oftest" Makefile target for running OFTest against Open
       vSwitch.  See README-OFTest for details.
     - The flow eviction threshold has been moved to the Open_vSwitch table.
     - Database names are now mandatory when specifying ovsdb-server options
       through database paths (e.g. Private key option with the database name
       should look like "--private-key=db:Open_vSwitch,SSL,private_key").
+    - Added ovs-dev.py, a utility script helpful for Open vSwitch developers.
 
 
 v1.11.0 - xx xxx xxxx
index e4f9991..2f3e474 100644 (file)
@@ -86,6 +86,7 @@ OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(1)
 OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(2)
 OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(4)
 OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(8)
+OVS_CHECK_POSIX_AIO
 
 OVS_ENABLE_OPTION([-Wall])
 OVS_ENABLE_OPTION([-Wno-sign-compare])
index 2f02f71..1de50c1 100644 (file)
@@ -972,9 +972,10 @@ static struct genl_ops dp_packet_genl_ops[] = {
 
 static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats)
 {
+       struct flow_table *table;
        int i;
-       struct flow_table *table = ovsl_dereference(dp->table);
 
+       table = rcu_dereference_check(dp->table, lockdep_ovsl_is_held());
        stats->n_flows = ovs_flow_tbl_count(table);
 
        stats->n_hit = stats->n_missed = stats->n_lost = 0;
@@ -1250,7 +1251,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr **a = info->attrs;
        struct ovs_header *ovs_header = info->userhdr;
-       struct sw_flow_key key;
+       struct sw_flow_key key, masked_key;
        struct sw_flow *flow = NULL;
        struct sw_flow_mask mask;
        struct sk_buff *reply;
@@ -1278,9 +1279,13 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                if (IS_ERR(acts))
                        goto error;
 
-               error = validate_and_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &key,  0, &acts);
-               if (error)
+               ovs_flow_key_mask(&masked_key, &key, &mask);
+               error = validate_and_copy_actions(a[OVS_FLOW_ATTR_ACTIONS],
+                                                 &masked_key, 0, &acts);
+               if (error) {
+                       OVS_NLERR("Flow actions may not be safe on all matching packets.\n");
                        goto err_kfree;
+               }
        } else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) {
                error = -EINVAL;
                goto error;
@@ -1323,6 +1328,9 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                }
                clear_stats(flow);
 
+               flow->key = masked_key;
+               flow->unmasked_key = key;
+
                /* Make sure mask is unique in the system */
                mask_p = ovs_sw_flow_mask_find(table, &mask);
                if (!mask_p) {
@@ -1340,7 +1348,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                rcu_assign_pointer(flow->sf_acts, acts);
 
                /* Put flow in bucket. */
-               ovs_flow_insert(table, flow, &key, match.range.end);
+               ovs_flow_insert(table, flow);
 
                reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
                                                info->snd_seq, OVS_FLOW_CMD_NEW);
@@ -1747,7 +1755,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
                goto err_destroy_local_port;
 
        ovs_net = net_generic(ovs_dp_get_net(dp), ovs_net_id);
-       list_add_tail(&dp->list_node, &ovs_net->dps);
+       list_add_tail_rcu(&dp->list_node, &ovs_net->dps);
 
        ovs_unlock();
 
@@ -1785,7 +1793,7 @@ static void __dp_destroy(struct datapath *dp)
                                ovs_dp_detach_port(vport);
        }
 
-       list_del(&dp->list_node);
+       list_del_rcu(&dp->list_node);
 
        /* OVSP_LOCAL is datapath internal port. We need to make sure that
         * all port in datapath are destroyed first before freeing datapath.
@@ -1902,8 +1910,8 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
        int skip = cb->args[0];
        int i = 0;
 
-       ovs_lock();
-       list_for_each_entry(dp, &ovs_net->dps, list_node) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(dp, &ovs_net->dps, list_node) {
                if (i >= skip &&
                    ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).portid,
                                         cb->nlh->nlmsg_seq, NLM_F_MULTI,
@@ -1911,7 +1919,7 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
                        break;
                i++;
        }
-       ovs_unlock();
+       rcu_read_unlock();
 
        cb->args[0] = i;
 
index d14a162..eda87fd 100644 (file)
@@ -206,6 +206,6 @@ int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb);
 void ovs_dp_notify_wq(struct work_struct *work);
 
 #define OVS_NLERR(fmt, ...) \
-       pr_info_once(fmt "netlink: ", ##__VA_ARGS__)
+       pr_info_once("netlink: " fmt, ##__VA_ARGS__)
 
 #endif /* datapath.h */
index c76c18d..752c8d6 100644 (file)
@@ -133,6 +133,10 @@ static bool ovs_match_validate(const struct sw_flow_match *match,
                        | (1ULL << OVS_KEY_ATTR_ARP)
                        | (1ULL << OVS_KEY_ATTR_ND));
 
+       if (match->key->phy.in_port == DP_MAX_PORTS &&
+           match->mask && (match->mask->key.phy.in_port == 0xffff))
+               mask_allowed |= (1ULL << OVS_KEY_ATTR_IN_PORT);
+
        if (match->key->eth.type == htons(ETH_P_802_2) &&
            match->mask && (match->mask->key.eth.type == htons(0xffff)))
                mask_allowed |= (1ULL << OVS_KEY_ATTR_ETHERTYPE);
@@ -345,9 +349,8 @@ static bool icmp6hdr_ok(struct sk_buff *skb)
                                  sizeof(struct icmp6hdr));
 }
 
-static void flow_key_mask(struct sw_flow_key *dst,
-                         const struct sw_flow_key *src,
-                         const struct sw_flow_mask *mask)
+void ovs_flow_key_mask(struct sw_flow_key *dst, const struct sw_flow_key *src,
+                      const struct sw_flow_mask *mask)
 {
        u8 *m = (u8 *)&mask->key + mask->range.start;
        u8 *s = (u8 *)src + mask->range.start;
@@ -1041,7 +1044,7 @@ static struct sw_flow *ovs_masked_flow_lookup(struct flow_table *table,
        u32 hash;
        struct sw_flow_key masked_key;
 
-       flow_key_mask(&masked_key, flow_key, mask);
+       ovs_flow_key_mask(&masked_key, flow_key, mask);
        hash = ovs_flow_hash(&masked_key, key_start, key_len);
        head = find_bucket(table, hash);
        hlist_for_each_entry_rcu(flow, head, hash_node[table->node_ver]) {
@@ -1067,11 +1070,8 @@ struct sw_flow *ovs_flow_lookup(struct flow_table *tbl,
 }
 
 
-void ovs_flow_insert(struct flow_table *table, struct sw_flow *flow,
-                        const struct sw_flow_key *key, int key_len)
+void ovs_flow_insert(struct flow_table *table, struct sw_flow *flow)
 {
-       flow->unmasked_key = *key;
-       flow_key_mask(&flow->key, &flow->unmasked_key, ovsl_dereference(flow->mask));
        flow->hash = ovs_flow_hash(&flow->key,
                        ovsl_dereference(flow->mask)->range.start,
                        ovsl_dereference(flow->mask)->range.end);
@@ -1197,14 +1197,14 @@ int ipv4_tun_from_nlattr(const struct nlattr *attr,
                };
 
                if (type > OVS_TUNNEL_KEY_ATTR_MAX) {
-                       OVS_NLERR("Unknown IPv4 tunnel attribute (type=%d, max=%d)\n",
+                       OVS_NLERR("Unknown IPv4 tunnel attribute (type=%d, max=%d).\n",
                        type, OVS_TUNNEL_KEY_ATTR_MAX);
                        return -EINVAL;
                }
 
                if (ovs_tunnel_key_lens[type] != nla_len(a)) {
                        OVS_NLERR("IPv4 tunnel attribute type has unexpected "
-                                 " legnth (type=%d, length=%d, expected=%d.)\n",
+                                 " legnth (type=%d, length=%d, expected=%d).\n",
                                  type, nla_len(a), ovs_tunnel_key_lens[type]);
                        return -EINVAL;
                }
@@ -1256,7 +1256,7 @@ int ipv4_tun_from_nlattr(const struct nlattr *attr,
        }
 
        if (!ttl) {
-               OVS_NLERR("IPv4 tunnel TTL is zero.\n");
+               OVS_NLERR("IPv4 tunnel TTL not specified.\n");
                return -EINVAL;
        }
 
@@ -1273,23 +1273,24 @@ int ipv4_tun_to_nlattr(struct sk_buff *skb,
        if (!nla)
                return -EMSGSIZE;
 
-       if (tun_key->tun_flags & TUNNEL_KEY &&
+       if (output->tun_flags & TUNNEL_KEY &&
            nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id))
                return -EMSGSIZE;
-       if (tun_key->ipv4_src &&
-           nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, output->ipv4_src))
+       if (output->ipv4_src &&
+               nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, output->ipv4_src))
                return -EMSGSIZE;
-       if (nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, output->ipv4_dst))
+       if (output->ipv4_dst &&
+               nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, output->ipv4_dst))
                return -EMSGSIZE;
-       if (tun_key->ipv4_tos &&
-           nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->ipv4_tos))
+       if (output->ipv4_tos &&
+               nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->ipv4_tos))
                return -EMSGSIZE;
        if (nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TTL, output->ipv4_ttl))
                return -EMSGSIZE;
-       if ((tun_key->tun_flags & TUNNEL_DONT_FRAGMENT) &&
+       if ((output->tun_flags & TUNNEL_DONT_FRAGMENT) &&
                nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT))
                return -EMSGSIZE;
-       if ((tun_key->tun_flags & TUNNEL_CSUM) &&
+       if ((output->tun_flags & TUNNEL_CSUM) &&
                nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_CSUM))
                return -EMSGSIZE;
 
@@ -1314,6 +1315,8 @@ static int metadata_from_nlattrs(struct sw_flow_match *match,  u64 *attrs,
                        return -EINVAL;
                SW_FLOW_KEY_PUT(match, phy.in_port, in_port, is_mask);
                *attrs &= ~(1ULL << OVS_KEY_ATTR_IN_PORT);
+       } else if (!is_mask) {
+               SW_FLOW_KEY_PUT(match, phy.in_port, DP_MAX_PORTS, is_mask);
        }
 
        if (*attrs & (1ULL << OVS_KEY_ATTR_SKB_MARK)) {
@@ -1686,26 +1689,29 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey,
        struct ovs_key_ethernet *eth_key;
        struct nlattr *nla, *encap;
 
-       if (swkey->phy.priority &&
-           nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority))
+       if (output->phy.priority &&
+               nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority))
                goto nla_put_failure;
 
        if (swkey->tun_key.ipv4_dst &&
            ipv4_tun_to_nlattr(skb, &swkey->tun_key, &output->tun_key))
                goto nla_put_failure;
 
-       if (swkey->phy.in_port != DP_MAX_PORTS) {
-               /* Exact match upper 16 bits. */
+       if (swkey->phy.in_port == DP_MAX_PORTS) {
+               if ((swkey != output) && (output->phy.in_port == 0xffff))
+                       if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, 0xffffffff))
+                               goto nla_put_failure;
+       } else {
                u16 upper_u16;
                upper_u16 = (swkey == output) ? 0 : 0xffff;
 
                if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT,
-                                       (upper_u16 << 16) | output->phy.in_port))
+                               (upper_u16 << 16) | output->phy.in_port))
                        goto nla_put_failure;
        }
 
-       if (swkey->phy.skb_mark &&
-           nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, output->phy.skb_mark))
+       if (output->phy.skb_mark &&
+               nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, output->phy.skb_mark))
                goto nla_put_failure;
 
        nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key));
@@ -1728,12 +1734,22 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey,
        } else
                encap = NULL;
 
-       if ((swkey == output) && (swkey->eth.type == htons(ETH_P_802_2)))
+       if (swkey->eth.type == htons(ETH_P_802_2)) {
+               /*
+                * Ethertype 802.2 is represented in the netlink with omitted
+                * OVS_KEY_ATTR_ETHERTYPE in the flow key attribute, and
+                * 0xffff in the mask attribute.  Ethertype can also
+                * be wildcarded.
+                */
+               if (swkey != output && output->eth.type)
+                       if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
+                                               output->eth.type))
+                               goto nla_put_failure;
                goto unencap;
+       }
 
-       if (output->eth.type != 0)
-               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type))
-                       goto nla_put_failure;
+       if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type))
+               goto nla_put_failure;
 
        if (swkey->eth.type == htons(ETH_P_IP)) {
                struct ovs_key_ipv4 *ipv4_key;
index a31dab0..1a3764e 100644 (file)
@@ -216,9 +216,8 @@ void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred);
 struct flow_table *ovs_flow_tbl_alloc(int new_size);
 struct flow_table *ovs_flow_tbl_expand(struct flow_table *table);
 struct flow_table *ovs_flow_tbl_rehash(struct flow_table *table);
-void ovs_flow_insert(struct flow_table *table, struct sw_flow *flow,
-               const struct sw_flow_key *key, int key_len);
 
+void ovs_flow_insert(struct flow_table *table, struct sw_flow *flow);
 void ovs_flow_remove(struct flow_table *table, struct sw_flow *flow);
 
 struct sw_flow *ovs_flow_dump_next(struct flow_table *table, u32 *bucket, u32 *idx);
@@ -258,4 +257,6 @@ void ovs_sw_flow_mask_del_ref(struct sw_flow_mask *, bool deferred);
 void ovs_sw_flow_mask_insert(struct flow_table *, struct sw_flow_mask *);
 struct sw_flow_mask *ovs_sw_flow_mask_find(const struct flow_table *,
                const struct sw_flow_mask *);
+void ovs_flow_key_mask(struct sw_flow_key *dst, const struct sw_flow_key *src,
+                      const struct sw_flow_mask *mask);
 #endif /* flow.h */
index 3cadde9..43418d3 100644 (file)
@@ -36,7 +36,7 @@
 
 #include "gso.h"
 
-static __be16 skb_network_protocol(struct sk_buff *skb)
+static __be16 __skb_network_protocol(struct sk_buff *skb)
 {
        __be16 type = skb->protocol;
        int vlan_depth = ETH_HLEN;
@@ -68,7 +68,7 @@ static struct sk_buff *tnl_skb_gso_segment(struct sk_buff *skb,
 
        /* setup whole inner packet to get protocol. */
        __skb_pull(skb, mac_offset);
-       skb->protocol = skb_network_protocol(skb);
+       skb->protocol = __skb_network_protocol(skb);
 
        /* setup l3 packet to gso, to get around segmentation bug on older kernel.*/
        __skb_pull(skb, (pkt_hlen - mac_offset));
index f2ced69..ba1fc59 100644 (file)
@@ -20,6 +20,11 @@ struct net;
 #define to_net_dev(class) container_of(class, struct net_device, NETDEV_DEV_MEMBER)
 #endif
 
+#ifdef HAVE_RHEL_OVS_HOOK
+extern struct sk_buff *(*openvswitch_handle_frame_hook)(struct sk_buff *skb);
+extern int nr_bridges;
+#endif
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
 static inline
 struct net *dev_net(const struct net_device *dev)
@@ -84,19 +89,33 @@ extern int skb_checksum_help(struct sk_buff *skb, int);
 extern int skb_checksum_help(struct sk_buff *skb);
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) || \
+    defined HAVE_RHEL_OVS_HOOK
 static inline int netdev_rx_handler_register(struct net_device *dev,
                                             void *rx_handler,
                                             void *rx_handler_data)
 {
+#ifdef HAVE_RHEL_OVS_HOOK
+       rcu_assign_pointer(dev->ax25_ptr, rx_handler_data);
+       nr_bridges++;
+       rcu_assign_pointer(openvswitch_handle_frame_hook, rx_handler_data);
+#else
        if (dev->br_port)
                return -EBUSY;
        rcu_assign_pointer(dev->br_port, rx_handler_data);
+#endif
        return 0;
 }
 static inline void netdev_rx_handler_unregister(struct net_device *dev)
 {
+#ifdef HAVE_RHEL_OVS_HOOK
+       rcu_assign_pointer(dev->ax25_ptr, NULL);
+
+       if (--nr_bridges <= 0)
+               rcu_assign_pointer(openvswitch_handle_frame_hook, NULL);
+#else
        rcu_assign_pointer(dev->br_port, NULL);
+#endif
 }
 #endif
 
index d26fb5e..f03efde 100644 (file)
@@ -1,6 +1,10 @@
 #include <linux/netdevice.h>
 #include <linux/if_vlan.h>
 
+#ifdef HAVE_RHEL_OVS_HOOK
+int nr_bridges = 0;
+#endif
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
 #ifndef HAVE_CAN_CHECKSUM_PROTOCOL
 static bool can_checksum_protocol(unsigned long features, __be16 protocol)
index 7f0b3c1..9ee1c42 100644 (file)
@@ -173,7 +173,7 @@ static void do_setup(struct net_device *netdev)
        netdev->hard_start_xmit = internal_dev_xmit;
        netdev->open = internal_dev_open;
        netdev->stop = internal_dev_stop;
-       netdev->set_mac_address = internal_dev_mac_addr;
+       netdev->set_mac_address = eth_mac_addr;
        netdev->change_mtu = internal_dev_change_mtu;
 #endif
 
index fe7e359..06598fa 100644 (file)
@@ -45,12 +45,6 @@ MODULE_PARM_DESC(vlan_tso, "Enable TSO for VLAN packets");
 #define vlan_tso true
 #endif
 
-#ifdef HAVE_RHEL_OVS_HOOK
-static atomic_t nr_bridges = ATOMIC_INIT(0);
-
-extern struct sk_buff *(*openvswitch_handle_frame_hook)(struct sk_buff *skb);
-#endif
-
 static void netdev_port_receive(struct vport *vport, struct sk_buff *skb);
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)
@@ -171,16 +165,10 @@ static struct vport *netdev_create(const struct vport_parms *parms)
        }
 
        rtnl_lock();
-#ifdef HAVE_RHEL_OVS_HOOK
-       rcu_assign_pointer(netdev_vport->dev->ax25_ptr, vport);
-       atomic_inc(&nr_bridges);
-       rcu_assign_pointer(openvswitch_handle_frame_hook, netdev_frame_hook);
-#else
        err = netdev_rx_handler_register(netdev_vport->dev, netdev_frame_hook,
                                         vport);
        if (err)
                goto error_unlock;
-#endif
 
        dev_set_promiscuity(netdev_vport->dev, 1);
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
@@ -192,9 +180,7 @@ static struct vport *netdev_create(const struct vport_parms *parms)
        netdev_init();
        return vport;
 
-#ifndef HAVE_RHEL_OVS_HOOK
 error_unlock:
-#endif
        rtnl_unlock();
 error_put:
        dev_put(netdev_vport->dev);
@@ -209,12 +195,6 @@ static void free_port_rcu(struct rcu_head *rcu)
        struct netdev_vport *netdev_vport = container_of(rcu,
                                        struct netdev_vport, rcu);
 
-#ifdef HAVE_RHEL_OVS_HOOK
-       rcu_assign_pointer(netdev_vport->dev->ax25_ptr, NULL);
-
-       if (atomic_dec_and_test(&nr_bridges))
-               rcu_assign_pointer(openvswitch_handle_frame_hook, NULL);
-#endif
        dev_put(netdev_vport->dev);
        ovs_vport_free(vport_from_priv(netdev_vport));
 }
index 7ba6a05..723c351 100644 (file)
@@ -33,6 +33,12 @@ int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) OVS_RELEASES(rwlock);
 int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *mutex)
     OVS_MUST_HOLD(mutex);
 
+/* Sparse complains about the proper PTHREAD_MUTEX_INITIALIZER definition.
+ * Luckily, it's not a real compiler so we can overwrite it with something
+ * simple. */
+#undef PTHREAD_MUTEX_INITIALIZER
+#define PTHREAD_MUTEX_INITIALIZER {}
+
 #define pthread_mutex_trylock(MUTEX)                    \
     ({                                                  \
         int retval = pthread_mutex_trylock(mutex);      \
diff --git a/lib/async-append-aio.c b/lib/async-append-aio.c
new file mode 100644 (file)
index 0000000..48edc38
--- /dev/null
@@ -0,0 +1,178 @@
+/* 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.
+ */
+
+#include <config.h>
+
+/* This implementation of the async-append.h interface uses the POSIX
+ * asynchronous I/O interface.  */
+
+#include "async-append.h"
+
+#include <aio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "byteq.h"
+#include "ovs-thread.h"
+#include "util.h"
+
+/* Maximum number of bytes of buffered data. */
+enum { BUFFER_SIZE = 65536 };
+
+/* Maximum number of aiocbs to use.
+ *
+ * aiocbs are big (144 bytes with glibc 2.11 on i386) so we try to allow for a
+ * reasonable number by basing the number we allocate on the amount of buffer
+ * space. */
+enum { MAX_CBS = ROUND_DOWN_POW2(BUFFER_SIZE / sizeof(struct aiocb)) };
+BUILD_ASSERT_DECL(IS_POW2(MAX_CBS));
+
+struct async_append {
+    int fd;
+
+    struct aiocb *aiocbs;
+    unsigned int aiocb_head, aiocb_tail;
+
+    uint8_t *buffer;
+    struct byteq byteq;
+};
+
+static bool async_append_enabled;
+
+void
+async_append_enable(void)
+{
+    assert_single_threaded();
+    forbid_forking("async i/o enabled");
+    async_append_enabled = true;
+}
+
+struct async_append *
+async_append_create(int fd)
+{
+    struct async_append *ap;
+
+    ap = xmalloc(sizeof *ap);
+    ap->fd = fd;
+    ap->aiocbs = xmalloc(MAX_CBS * sizeof *ap->aiocbs);
+    ap->aiocb_head = ap->aiocb_tail = 0;
+    ap->buffer = xmalloc(BUFFER_SIZE);
+    byteq_init(&ap->byteq, ap->buffer, BUFFER_SIZE);
+
+    return ap;
+}
+
+void
+async_append_destroy(struct async_append *ap)
+{
+    if (ap) {
+        async_append_flush(ap);
+        free(ap->aiocbs);
+        free(ap->buffer);
+        free(ap);
+    }
+}
+
+static bool
+async_append_is_full(const struct async_append *ap)
+{
+    return (ap->aiocb_head - ap->aiocb_tail >= MAX_CBS
+            || byteq_is_full(&ap->byteq));
+}
+
+static bool
+async_append_is_empty(const struct async_append *ap)
+{
+    return byteq_is_empty(&ap->byteq);
+}
+
+static void
+async_append_wait(struct async_append *ap)
+{
+    int n = 0;
+
+    while (!async_append_is_empty(ap)) {
+        struct aiocb *aiocb = &ap->aiocbs[ap->aiocb_tail & (MAX_CBS - 1)];
+        int error = aio_error(aiocb);
+
+        if (error == EINPROGRESS) {
+            const struct aiocb *p = aiocb;
+            if (n > 0) {
+                return;
+            }
+            aio_suspend(&p, 1, NULL);
+        } else {
+            ignore(aio_return(aiocb));
+            ap->aiocb_tail++;
+            byteq_advance_tail(&ap->byteq, aiocb->aio_nbytes);
+            n++;
+        }
+    }
+}
+
+void
+async_append_write(struct async_append *ap, const void *data_, size_t size)
+{
+    const uint8_t *data = data_;
+
+    if (!async_append_enabled) {
+        ignore(write(ap->fd, data, size));
+        return;
+    }
+
+    while (size > 0) {
+        struct aiocb *aiocb;
+        size_t chunk_size;
+        void *chunk;
+
+        while (async_append_is_full(ap)) {
+            async_append_wait(ap);
+        }
+
+        chunk = byteq_head(&ap->byteq);
+        chunk_size = byteq_headroom(&ap->byteq);
+        if (chunk_size > size) {
+            chunk_size = size;
+        }
+        memcpy(chunk, data, chunk_size);
+
+        aiocb = &ap->aiocbs[ap->aiocb_head & (MAX_CBS - 1)];
+        memset(aiocb, 0, sizeof *aiocb);
+        aiocb->aio_fildes = ap->fd;
+        aiocb->aio_offset = 0;
+        aiocb->aio_buf = chunk;
+        aiocb->aio_nbytes = chunk_size;
+        aiocb->aio_sigevent.sigev_notify = SIGEV_NONE;
+        if (aio_write(aiocb) == -1) {
+            async_append_flush(ap);
+            ignore(write(ap->fd, data, size));
+            return;
+        }
+
+        data += chunk_size;
+        size -= chunk_size;
+        byteq_advance_head(&ap->byteq, chunk_size);
+        ap->aiocb_head++;
+    }
+}
+
+void
+async_append_flush(struct async_append *ap)
+{
+    while (!async_append_is_empty(ap)) {
+        async_append_wait(ap);
+    }
+}
diff --git a/lib/async-append-sync.c b/lib/async-append-sync.c
new file mode 100644 (file)
index 0000000..d40fdc8
--- /dev/null
@@ -0,0 +1,62 @@
+/* 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.
+ */
+
+#include <config.h>
+
+/* This implementation of the async-append.h interface uses ordinary
+ * synchronous I/O, so it should be portable everywhere. */
+
+#include "async-append.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "util.h"
+
+struct async_append {
+    int fd;
+};
+
+void
+async_append_enable(void)
+{
+    /* Nothing to do. */
+}
+
+struct async_append *
+async_append_create(int fd)
+{
+    struct async_append *ap = xmalloc(sizeof *ap);
+    ap->fd = fd;
+    return ap;
+}
+
+void
+async_append_destroy(struct async_append *ap)
+{
+    free(ap);
+}
+
+void
+async_append_write(struct async_append *ap, const void *data, size_t size)
+{
+    ignore(write(ap->fd, data, size));
+}
+
+void
+async_append_flush(struct async_append *ap OVS_UNUSED)
+{
+    /* Nothing to do. */
+}
diff --git a/lib/async-append.h b/lib/async-append.h
new file mode 100644 (file)
index 0000000..fb0ce52
--- /dev/null
@@ -0,0 +1,67 @@
+/* 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.
+ */
+
+#ifndef ASYNC_APPEND_H
+#define ASYNC_APPEND_H 1
+
+#include <stddef.h>
+
+/* This module defines a simple, abstract interface to asynchronous file I/O.
+ * It is currently used only for logging.  Thus, for now the interface only
+ * supports appending to a file.  Multiple implementations are possible
+ * depending on the operating system's degree and form of support for
+ * asynchronous I/O.
+ *
+ * The comments below document the requirements on any implementation.
+ *
+ * Thread-safety
+ * =============
+ *
+ * Only a single thread may use a given 'struct async_append' at one time.
+ */
+
+/* Enables using asynchronous I/O.  Some implementations may treat this as a
+ * no-op.
+ *
+ * Before this function is called, the POSIX aio implementation uses ordinary
+ * synchronous I/O because some POSIX aio libraries actually use threads
+ * internally, which has enough cost and robustness implications that it's
+ * better to use asynchronous I/O only when it has real expected benefits.
+ *
+ * Must be called while the process is still single-threaded.  May forbid the
+ * process from subsequently forking. */
+void async_append_enable(void);
+
+/* Creates and returns a new asynchronous appender for file descriptor 'fd',
+ * which the caller must have opened in append mode (O_APPEND).
+ *
+ * This function must always succeed.  If the system is for some reason unable
+ * to support asynchronous I/O on 'fd' then the library must fall back to
+ * syncrhonous I/O. */
+struct async_append *async_append_create(int fd);
+
+/* Destroys 'ap', without closing its underlying file descriptor. */
+void async_append_destroy(struct async_append *ap);
+
+/* Appends the 'size' bytes of 'data' to 'ap', asynchronously if possible. */
+void async_append_write(struct async_append *ap,
+                        const void *data, size_t size);
+
+/* Blocks until all data asynchronously written to 'ap' with
+ * async_append_write() has been committed to the point that it will be written
+ * to disk barring an operating system or hardware failure. */
+void async_append_flush(struct async_append *ap);
+
+#endif /* async-append.h */
index 0804956..22a9b65 100644 (file)
@@ -10,6 +10,7 @@ noinst_LIBRARIES += lib/libopenvswitch.a
 lib_libopenvswitch_a_SOURCES = \
        lib/aes128.c \
        lib/aes128.h \
+       lib/async-append.h \
        lib/backtrace.c \
        lib/backtrace.h \
        lib/bfd.c \
@@ -71,6 +72,8 @@ lib_libopenvswitch_a_SOURCES = \
        lib/jsonrpc.h \
        lib/lacp.c \
        lib/lacp.h \
+       lib/latch.c \
+       lib/latch.h \
        lib/learn.c \
        lib/learn.h \
        lib/learning-switch.c \
@@ -184,8 +187,6 @@ lib_libopenvswitch_a_SOURCES = \
        lib/stream-unix.c \
        lib/stream.c \
        lib/stream.h \
-       lib/stress.c \
-       lib/stress.h \
        lib/string.c \
        lib/string.h \
        lib/svec.c \
@@ -224,9 +225,7 @@ lib_libopenvswitch_a_SOURCES = \
        lib/vlog.c \
        lib/vlog.h \
        lib/vswitch-idl.c \
-       lib/vswitch-idl.h \
-       lib/worker.c \
-       lib/worker.h
+       lib/vswitch-idl.h
 
 nodist_lib_libopenvswitch_a_SOURCES = \
        lib/dirs.c
@@ -265,6 +264,12 @@ lib_libopenvswitch_a_SOURCES += \
        lib/route-table.h
 endif
 
+if HAVE_POSIX_AIO
+lib_libopenvswitch_a_SOURCES += lib/async-append-aio.c
+else
+lib_libopenvswitch_a_SOURCES += lib/async-append-sync.c
+endif
+
 if ESX
 lib_libopenvswitch_a_SOURCES += \
         lib/route-table-stub.c
@@ -312,7 +317,6 @@ MAN_FRAGMENTS += \
        lib/ssl-peer-ca-cert.man \
        lib/ssl.man \
        lib/ssl-syn.man \
-       lib/stress-unixctl.man \
        lib/table.man \
        lib/unixctl.man \
        lib/unixctl-syn.man \
@@ -389,11 +393,6 @@ lib/coverage.def: $(DIST_SOURCES)
        sed -n 's|^COVERAGE_DEFINE(\([_a-zA-Z0-9]\{1,\}\)).*$$|COVERAGE_COUNTER(\1)|p' $(all_sources) | LC_ALL=C sort -u > $@
 CLEANFILES += lib/coverage.def
 
-lib/stress.$(OBJEXT): lib/stress.def
-lib/stress.def: $(DIST_SOURCES)
-       sed -n '/^STRESS_OPTION(/,/);$$/{s/);$$/)/;p}' $(all_sources) > $@
-CLEANFILES += lib/stress.def
-
 lib/vlog.$(OBJEXT): lib/vlog-modules.def
 lib/vlog-modules.def: $(DIST_SOURCES)
        sed -n 's|^VLOG_DEFINE_\(THIS_\)\{0,1\}MODULE(\([_a-zA-Z0-9]\{1,\}\)).*$$|VLOG_MODULE(\2)|p' $(all_sources) | LC_ALL=C sort -u > $@
index 8039c09..3ac257a 100644 (file)
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -17,6 +17,7 @@
 
 #include <arpa/inet.h>
 
+#include "byte-order.h"
 #include "csum.h"
 #include "dpif.h"
 #include "dynamic-string.h"
@@ -58,10 +59,6 @@ VLOG_DEFINE_THIS_MODULE(bfd);
  *
  * - Set TOS/PCP on inner BFD frame, and outer tunnel header when encapped.
  *
- * - CFM "check_tnl_key" option equivalent.
- *
- * - CFM "fault override" equivalent.
- *
  * - Sending BFD messages should be in its own thread/process.
  *
  * - Scale testing.  How does it operate when there are large number of bfd
@@ -181,6 +178,8 @@ struct bfd {
     long long int detect_time;    /* RFC 5880 6.8.4 Detection time. */
 
     int ref_cnt;
+    int forwarding_override;      /* Manual override of 'forwarding' status. */
+    bool check_tnl_key;           /* Verify tunnel key of inbound packets? */
 };
 
 static bool bfd_in_poll(const struct bfd *);
@@ -196,6 +195,9 @@ static uint32_t generate_discriminator(void);
 static void bfd_put_details(struct ds *, const struct bfd *);
 static void bfd_unixctl_show(struct unixctl_conn *, int argc,
                              const char *argv[], void *aux OVS_UNUSED);
+static void bfd_unixctl_set_forwarding_override(struct unixctl_conn *,
+                                                int argc, const char *argv[],
+                                                void *aux OVS_UNUSED);
 static void log_msg(enum vlog_level, const struct msg *, const char *message,
                     const struct bfd *);
 
@@ -207,6 +209,10 @@ static struct hmap all_bfds = HMAP_INITIALIZER(&all_bfds);
 bool
 bfd_forwarding(const struct bfd *bfd)
 {
+    if (bfd->forwarding_override != -1) {
+        return bfd->forwarding_override == 1;
+    }
+
     return bfd->state == STATE_UP
         && bfd->rmt_diag != DIAG_PATH_DOWN
         && bfd->rmt_diag != DIAG_CPATH_DOWN
@@ -246,6 +252,9 @@ bfd_configure(struct bfd *bfd, const char *name,
     if (!init) {
         unixctl_command_register("bfd/show", "[interface]", 0, 1,
                                  bfd_unixctl_show, NULL);
+        unixctl_command_register("bfd/set-forwarding",
+                                 "[interface] normal|false|true", 1, 2,
+                                 bfd_unixctl_set_forwarding_override, NULL);
         init = true;
     }
 
@@ -257,6 +266,7 @@ bfd_configure(struct bfd *bfd, const char *name,
     if (!bfd) {
         bfd = xzalloc(sizeof *bfd);
         bfd->name = xstrdup(name);
+        bfd->forwarding_override = -1;
         bfd->disc = generate_discriminator();
         hmap_insert(&all_bfds, &bfd->node, bfd->disc);
 
@@ -275,6 +285,7 @@ bfd_configure(struct bfd *bfd, const char *name,
         bfd_set_state(bfd, STATE_DOWN, DIAG_NONE);
     }
 
+    bfd->check_tnl_key = smap_get_bool(cfg, "check_tnl_key", false);
     min_tx = smap_get_int(cfg, "min_tx", 100);
     min_tx = MAX(min_tx, 100);
     if (bfd->cfg_min_tx != min_tx) {
@@ -437,13 +448,18 @@ bfd_put_packet(struct bfd *bfd, struct ofpbuf *p,
 }
 
 bool
-bfd_should_process_flow(const struct flow *flow, struct flow_wildcards *wc)
+bfd_should_process_flow(const struct bfd *bfd, const struct flow *flow,
+                        struct flow_wildcards *wc)
 {
     memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
     memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
+    if (bfd->check_tnl_key) {
+        memset(&wc->masks.tunnel.tun_id, 0xff, sizeof wc->masks.tunnel.tun_id);
+    }
     return (flow->dl_type == htons(ETH_TYPE_IP)
             && flow->nw_proto == IPPROTO_UDP
-            && flow->tp_dst == htons(3784));
+            && flow->tp_dst == htons(3784)
+            && (!bfd->check_tnl_key || flow->tunnel.tun_id == htonll(0)));
 }
 
 void
@@ -897,3 +913,39 @@ bfd_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[],
     unixctl_command_reply(conn, ds_cstr(&ds));
     ds_destroy(&ds);
 }
+
+
+static void
+bfd_unixctl_set_forwarding_override(struct unixctl_conn *conn, int argc,
+                                    const char *argv[], void *aux OVS_UNUSED)
+{
+    const char *forward_str = argv[argc - 1];
+    int forwarding_override;
+    struct bfd *bfd;
+
+    if (!strcasecmp("true", forward_str)) {
+        forwarding_override = 1;
+    } else if (!strcasecmp("false", forward_str)) {
+        forwarding_override = 0;
+    } else if (!strcasecmp("normal", forward_str)) {
+        forwarding_override = -1;
+    } else {
+        unixctl_command_reply_error(conn, "unknown fault string");
+        return;
+    }
+
+    if (argc > 2) {
+        bfd = bfd_find_by_name(argv[1]);
+        if (!bfd) {
+            unixctl_command_reply_error(conn, "no such BFD object");
+            return;
+        }
+        bfd->forwarding_override = forwarding_override;
+    } else {
+        HMAP_FOR_EACH (bfd, node, &all_bfds) {
+            bfd->forwarding_override = forwarding_override;
+        }
+    }
+
+    unixctl_command_reply(conn, "OK");
+}
index ab854d8..67d012e 100644 (file)
--- a/lib/bfd.h
+++ b/lib/bfd.h
@@ -34,7 +34,8 @@ bool bfd_should_send_packet(const struct bfd *);
 void bfd_put_packet(struct bfd *bfd, struct ofpbuf *packet,
                     uint8_t eth_src[6]);
 
-bool bfd_should_process_flow(const struct flow *, struct flow_wildcards *);
+bool bfd_should_process_flow(const struct bfd *, const struct flow *,
+                             struct flow_wildcards *);
 void bfd_process_packet(struct bfd *, const struct flow *,
                         const struct ofpbuf *);
 
index d607526..ac568e9 100644 (file)
@@ -24,6 +24,7 @@ bitmap_allocate1(size_t n_bits)
 {
     size_t n_bytes = bitmap_n_bytes(n_bits);
     size_t n_longs = bitmap_n_longs(n_bits);
+    size_t r_bits = n_bits % BITMAP_ULONG_BITS;
     unsigned long *bitmap;
 
     /* Allocate and initialize most of the bitmap. */
@@ -32,7 +33,9 @@ bitmap_allocate1(size_t n_bits)
 
     /* Ensure that the last "unsigned long" in the bitmap only has as many
      * 1-bits as there actually should be. */
-    bitmap[n_longs - 1] = (1UL << (n_bits % BITMAP_ULONG_BITS)) - 1;
+    if (r_bits) {
+        bitmap[n_longs - 1] = (1UL << r_bits) - 1;
+    }
 
     return bitmap;
 }
index 78a297a..dcabaaa 100644 (file)
@@ -1,4 +1,4 @@
-/* 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.
@@ -251,8 +251,11 @@ bundle_to_nxast(const struct ofpact_bundle *bundle, struct ofpbuf *openflow)
     }
 }
 
-/* Helper for bundle_parse and bundle_parse_load. */
-static void
+/* Helper for bundle_parse and bundle_parse_load.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string.*/
+static char * WARN_UNUSED_RESULT
 bundle_parse__(const char *s, char **save_ptr,
                const char *fields, const char *basis, const char *algorithm,
                const char *slave_type, const char *dst,
@@ -261,12 +264,12 @@ bundle_parse__(const char *s, char **save_ptr,
     struct ofpact_bundle *bundle;
 
     if (!slave_delim) {
-        ovs_fatal(0, "%s: not enough arguments to bundle action", s);
+        return xasprintf("%s: not enough arguments to bundle action", s);
     }
 
     if (strcasecmp(slave_delim, "slaves")) {
-        ovs_fatal(0, "%s: missing slave delimiter, expected `slaves' got `%s'",
-                   s, slave_delim);
+        return xasprintf("%s: missing slave delimiter, expected `slaves' "
+                         "got `%s'", s, slave_delim);
     }
 
     bundle = ofpact_put_BUNDLE(ofpacts);
@@ -281,7 +284,7 @@ bundle_parse__(const char *s, char **save_ptr,
         }
 
         if (!ofputil_port_from_string(slave, &slave_port)) {
-            ovs_fatal(0, "%s: bad port number", slave);
+            return xasprintf("%s: bad port number", slave);
         }
         ofpbuf_put(ofpacts, &slave_port, sizeof slave_port);
 
@@ -297,7 +300,7 @@ bundle_parse__(const char *s, char **save_ptr,
     } else if (!strcasecmp(fields, "symmetric_l4")) {
         bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L4;
     } else {
-        ovs_fatal(0, "%s: unknown fields `%s'", s, fields);
+        return xasprintf("%s: unknown fields `%s'", s, fields);
     }
 
     if (!strcasecmp(algorithm, "active_backup")) {
@@ -305,25 +308,34 @@ bundle_parse__(const char *s, char **save_ptr,
     } else if (!strcasecmp(algorithm, "hrw")) {
         bundle->algorithm = NX_BD_ALG_HRW;
     } else {
-        ovs_fatal(0, "%s: unknown algorithm `%s'", s, algorithm);
+        return xasprintf("%s: unknown algorithm `%s'", s, algorithm);
     }
 
     if (strcasecmp(slave_type, "ofport")) {
-        ovs_fatal(0, "%s: unknown slave_type `%s'", s, slave_type);
+        return xasprintf("%s: unknown slave_type `%s'", s, slave_type);
     }
 
     if (dst) {
-        mf_parse_subfield(&bundle->dst, dst);
+        char *error = mf_parse_subfield(&bundle->dst, dst);
+        if (error) {
+            return error;
+        }
     }
+
+    return NULL;
 }
 
 /* Converts a bundle action string contained in 's' to an nx_action_bundle and
- * stores it in 'b'.  Sets 'b''s l2 pointer to NULL. */
-void
+ * stores it in 'b'.  Sets 'b''s l2 pointer to NULL.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+char * WARN_UNUSED_RESULT
 bundle_parse(const char *s, struct ofpbuf *ofpacts)
 {
     char *fields, *basis, *algorithm, *slave_type, *slave_delim;
     char *tokstr, *save_ptr;
+    char *error;
 
     save_ptr = NULL;
     tokstr = xstrdup(s);
@@ -333,18 +345,24 @@ bundle_parse(const char *s, struct ofpbuf *ofpacts)
     slave_type = strtok_r(NULL, ", ", &save_ptr);
     slave_delim = strtok_r(NULL, ": ", &save_ptr);
 
-    bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, NULL,
-                   slave_delim, ofpacts);
+    error = bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type,
+                           NULL, slave_delim, ofpacts);
     free(tokstr);
+
+    return error;
 }
 
 /* Converts a bundle_load action string contained in 's' to an nx_action_bundle
- * and stores it in 'b'.  Sets 'b''s l2 pointer to NULL. */
-void
+ * and stores it in 'b'.  Sets 'b''s l2 pointer to NULL.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string.*/
+char * WARN_UNUSED_RESULT
 bundle_parse_load(const char *s, struct ofpbuf *ofpacts)
 {
     char *fields, *basis, *algorithm, *slave_type, *dst, *slave_delim;
     char *tokstr, *save_ptr;
+    char *error;
 
     save_ptr = NULL;
     tokstr = xstrdup(s);
@@ -355,10 +373,12 @@ bundle_parse_load(const char *s, struct ofpbuf *ofpacts)
     dst = strtok_r(NULL, ", ", &save_ptr);
     slave_delim = strtok_r(NULL, ": ", &save_ptr);
 
-    bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, dst,
-                   slave_delim, ofpacts);
+    error = bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type,
+                           dst, slave_delim, ofpacts);
 
     free(tokstr);
+
+    return error;
 }
 
 /* Appends a human-readable representation of 'nab' to 's'. */
index 3e92374..dceb6e5 100644 (file)
@@ -1,4 +1,4 @@
-/* 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.
@@ -21,6 +21,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include "compiler.h"
 #include "ofp-errors.h"
 #include "openflow/nicira-ext.h"
 #include "openvswitch/types.h"
@@ -44,8 +45,9 @@ enum ofperr bundle_from_openflow(const struct nx_action_bundle *,
 enum ofperr bundle_check(const struct ofpact_bundle *, ofp_port_t max_ports,
                          const struct flow *);
 void bundle_to_nxast(const struct ofpact_bundle *, struct ofpbuf *of10);
-void bundle_parse(const char *, struct ofpbuf *ofpacts);
-void bundle_parse_load(const char *, struct ofpbuf *ofpacts);
+char *bundle_parse(const char *, struct ofpbuf *ofpacts) WARN_UNUSED_RESULT;
+char *bundle_parse_load(const char *, struct ofpbuf *ofpacts)
+    WARN_UNUSED_RESULT;
 void bundle_format(const struct ofpact_bundle *, struct ds *);
 
 #endif /* bundle.h */
index 2ee4a65..3f865cf 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009, 2012 Nicira, Inc.
+/* Copyright (c) 2008, 2009, 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.
 #include <unistd.h>
 #include "util.h"
 
-/* The queue size must be a power of 2. */
-BUILD_ASSERT_DECL(!(BYTEQ_SIZE & (BYTEQ_SIZE - 1)));
-
-/* Initializes 'q' as empty. */
+/* Initializes 'q' as an empty byteq that uses the 'size' bytes of 'buffer' to
+ * store data.  'size' must be a power of 2.
+ *
+ * The caller must ensure that 'buffer' remains available to the byteq as long
+ * as 'q' is in use. */
 void
-byteq_init(struct byteq *q)
+byteq_init(struct byteq *q, uint8_t *buffer, size_t size)
 {
+    ovs_assert(is_pow2(size));
+    q->buffer = buffer;
+    q->size = size;
     q->head = q->tail = 0;
 }
 
@@ -41,7 +45,7 @@ byteq_used(const struct byteq *q)
 int
 byteq_avail(const struct byteq *q)
 {
-    return BYTEQ_SIZE - byteq_used(q);
+    return q->size - byteq_used(q);
 }
 
 /* Returns true if no bytes are queued in 'q',
@@ -147,7 +151,7 @@ int
 byteq_tailroom(const struct byteq *q)
 {
     int used = byteq_used(q);
-    int tail_to_end = BYTEQ_SIZE - (q->tail & (BYTEQ_SIZE - 1));
+    int tail_to_end = q->size - (q->tail & (q->size - 1));
     return MIN(used, tail_to_end);
 }
 
@@ -156,7 +160,7 @@ byteq_tailroom(const struct byteq *q)
 const uint8_t *
 byteq_tail(const struct byteq *q)
 {
-    return &q->buffer[q->tail & (BYTEQ_SIZE - 1)];
+    return &q->buffer[q->tail & (q->size - 1)];
 }
 
 /* Removes 'n' bytes from the tail of 'q', which must have at least 'n' bytes
@@ -173,7 +177,7 @@ byteq_advance_tail(struct byteq *q, unsigned int n)
 uint8_t *
 byteq_head(struct byteq *q)
 {
-    return &q->buffer[q->head & (BYTEQ_SIZE - 1)];
+    return &q->buffer[q->head & (q->size - 1)];
 }
 
 /* Returns the number of contiguous bytes of free space starting at the head
@@ -182,7 +186,7 @@ int
 byteq_headroom(const struct byteq *q)
 {
     int avail = byteq_avail(q);
-    int head_to_end = BYTEQ_SIZE - (q->head & (BYTEQ_SIZE - 1));
+    int head_to_end = q->size - (q->head & (q->size - 1));
     return MIN(avail, head_to_end);
 }
 
index 5fa51fd..d73e368 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009 Nicira, Inc.
+/* Copyright (c) 2008, 2009, 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.
 #include <stddef.h>
 #include <stdint.h>
 
-/* Maximum number of bytes in a byteq. */
-#define BYTEQ_SIZE 512
-
 /* General-purpose circular queue of bytes. */
 struct byteq {
-    uint8_t buffer[BYTEQ_SIZE]; /* Circular queue. */
+    uint8_t *buffer;            /* Circular queue. */
+    unsigned int size;          /* Number of bytes allocated for 'buffer'. */
     unsigned int head;          /* Head of queue. */
     unsigned int tail;          /* Chases the head. */
 };
 
-void byteq_init(struct byteq *);
+void byteq_init(struct byteq *, uint8_t *buffer, size_t size);
 int byteq_used(const struct byteq *);
 int byteq_avail(const struct byteq *);
 bool byteq_is_empty(const struct byteq *);
index 06415fc..a76a3ec 100644 (file)
--- a/lib/cfm.c
+++ b/lib/cfm.c
@@ -61,6 +61,8 @@ static const uint8_t eth_addr_ccm_x[6] = {
 #define CCM_OPCODE 1 /* CFM message opcode meaning CCM. */
 #define CCM_RDI_MASK 0x80
 #define CFM_HEALTH_INTERVAL 6
+
+OVS_PACKED(
 struct ccm {
     uint8_t mdlevel_version; /* MD Level and Version */
     uint8_t opcode;
@@ -78,7 +80,7 @@ struct ccm {
 
     /* TLV space. */
     uint8_t end_tlv;
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(CCM_LEN == sizeof(struct ccm));
 
 struct cfm {
index f3cbe96..4b1834f 100644 (file)
 #define OVS_PACKED_ENUM
 #endif
 
+#ifndef _MSC_VER
+#define OVS_PACKED(DECL) DECL __attribute__((__packed__))
+#else
+#define OVS_PACKED(DECL) __pragma(pack(push, 1)) DECL __pragma(pack(pop))
+#endif
+
 #endif /* compiler.h */
index 3c1e5c3..98f30d7 100644 (file)
@@ -400,7 +400,7 @@ monitor_daemon(pid_t daemon_pid)
     char *status_msg;
     int crashes;
 
-    subprogram_name = "monitor";
+    set_subprogram_name("monitor");
     status_msg = xstrdup("healthy");
     last_restart = TIME_MIN;
     crashes = 0;
@@ -470,7 +470,7 @@ monitor_daemon(pid_t daemon_pid)
 
     /* Running in new daemon process. */
     proctitle_restore();
-    subprogram_name = "";
+    set_subprogram_name("");
 }
 
 /* Close standard file descriptors (except any that the client has requested we
@@ -529,6 +529,8 @@ daemonize_start(void)
         /* Running in daemon process. */
     }
 
+    forbid_forking("running in daemon process");
+
     if (pidfile) {
         make_pidfile();
     }
index 804a90f..958873c 100644 (file)
@@ -162,7 +162,6 @@ static int ovs_flow_family;
 static int ovs_packet_family;
 
 /* Generic Netlink socket. */
-static struct nl_sock *genl_sock;
 static struct nln *nln = NULL;
 
 static int dpif_linux_init(void);
@@ -692,7 +691,7 @@ dpif_linux_port_dump_start(const struct dpif *dpif_, void **statep)
 
     buf = ofpbuf_new(1024);
     dpif_linux_vport_to_ofpbuf(&request, buf);
-    nl_dump_start(&state->dump, genl_sock, buf);
+    nl_dump_start(&state->dump, NETLINK_GENERIC, buf);
     ofpbuf_delete(buf);
 
     return 0;
@@ -898,7 +897,7 @@ dpif_linux_flow_dump_start(const struct dpif *dpif_, void **statep)
 
     buf = ofpbuf_new(1024);
     dpif_linux_flow_to_ofpbuf(&request, buf);
-    nl_dump_start(&state->dump, genl_sock, buf);
+    nl_dump_start(&state->dump, NETLINK_GENERIC, buf);
     ofpbuf_delete(buf);
 
     state->buf = NULL;
@@ -1005,7 +1004,7 @@ dpif_linux_execute__(int dp_ifindex, const struct dpif_execute *execute)
 
     ofpbuf_use_stub(&request, request_stub, sizeof request_stub);
     dpif_linux_encode_execute(dp_ifindex, execute, &request);
-    error = nl_sock_transact(genl_sock, &request, NULL);
+    error = nl_transact(NETLINK_GENERIC, &request, NULL);
     ofpbuf_uninit(&request);
 
     return error;
@@ -1090,7 +1089,7 @@ dpif_linux_operate__(struct dpif *dpif_, struct dpif_op **ops, size_t n_ops)
     for (i = 0; i < n_ops; i++) {
         txnsp[i] = &auxes[i].txn;
     }
-    nl_sock_transact_multiple(genl_sock, txnsp, n_ops);
+    nl_transact_multiple(NETLINK_GENERIC, txnsp, n_ops);
 
     for (i = 0; i < n_ops; i++) {
         struct op_auxdata *aux = &auxes[i];
@@ -1463,9 +1462,6 @@ dpif_linux_init(void)
             error = nl_lookup_genl_family(OVS_PACKET_FAMILY,
                                           &ovs_packet_family);
         }
-        if (!error) {
-            error = nl_sock_create(NETLINK_GENERIC, &genl_sock);
-        }
         if (!error) {
             error = nl_lookup_genl_mcgroup(OVS_VPORT_FAMILY, OVS_VPORT_MCGROUP,
                                            &ovs_vport_mcgroup,
@@ -1659,7 +1655,7 @@ dpif_linux_vport_transact(const struct dpif_linux_vport *request,
 
     request_buf = ofpbuf_new(1024);
     dpif_linux_vport_to_ofpbuf(request, request_buf);
-    error = nl_sock_transact(genl_sock, request_buf, bufp);
+    error = nl_transact(NETLINK_GENERIC, request_buf, bufp);
     ofpbuf_delete(request_buf);
 
     if (reply) {
@@ -1780,7 +1776,7 @@ dpif_linux_dp_dump_start(struct nl_dump *dump)
 
     buf = ofpbuf_new(1024);
     dpif_linux_dp_to_ofpbuf(&request, buf);
-    nl_dump_start(dump, genl_sock, buf);
+    nl_dump_start(dump, NETLINK_GENERIC, buf);
     ofpbuf_delete(buf);
 }
 
@@ -1801,7 +1797,7 @@ dpif_linux_dp_transact(const struct dpif_linux_dp *request,
 
     request_buf = ofpbuf_new(1024);
     dpif_linux_dp_to_ofpbuf(request, request_buf);
-    error = nl_sock_transact(genl_sock, request_buf, bufp);
+    error = nl_transact(NETLINK_GENERIC, request_buf, bufp);
     ofpbuf_delete(request_buf);
 
     if (reply) {
@@ -1965,7 +1961,7 @@ dpif_linux_flow_transact(struct dpif_linux_flow *request,
 
     request_buf = ofpbuf_new(1024);
     dpif_linux_flow_to_ofpbuf(request, request_buf);
-    error = nl_sock_transact(genl_sock, request_buf, bufp);
+    error = nl_transact(NETLINK_GENERIC, request_buf, bufp);
     ofpbuf_delete(request_buf);
 
     if (reply) {
index 48d8e27..afb2ef6 100644 (file)
@@ -702,6 +702,8 @@ static int
 dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len,
                               struct flow *flow)
 {
+    odp_port_t in_port;
+
     if (odp_flow_key_to_flow(key, key_len, flow) != ODP_FIT_PERFECT) {
         /* This should not happen: it indicates that odp_flow_key_from_flow()
          * and odp_flow_key_to_flow() disagree on the acceptable form of a
@@ -721,7 +723,8 @@ dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len,
         return EINVAL;
     }
 
-    if (!is_valid_port_number(flow->in_port.odp_port)) {
+    in_port = flow->in_port.odp_port;
+    if (!is_valid_port_number(in_port) && in_port != ODPP_NONE) {
         return EINVAL;
     }
 
index 3cccb5c..9b3e7ba 100644 (file)
@@ -238,14 +238,21 @@ ds_get_line(struct ds *ds, FILE *file)
  * Deletes comments introduced by "#" and skips lines that contains only white
  * space (after deleting comments).
  *
+ * If 'line_numberp' is nonnull, increments '*line_numberp' by the number of
+ * lines read from 'file'.
+ *
  * Returns 0 if successful, EOF if no non-blank line was found. */
 int
-ds_get_preprocessed_line(struct ds *ds, FILE *file)
+ds_get_preprocessed_line(struct ds *ds, FILE *file, int *line_numberp)
 {
     while (!ds_get_line(ds, file)) {
         char *line = ds_cstr(ds);
         char *comment;
 
+        if (line_numberp) {
+            ++*line_numberp;
+        }
+
         /* Delete comments. */
         comment = strchr(line, '#');
         if (comment) {
index b988e1f..c069586 100644 (file)
@@ -58,7 +58,7 @@ void ds_put_printable(struct ds *, const char *, size_t);
 void ds_put_hex_dump(struct ds *ds, const void *buf_, size_t size,
                      uintptr_t ofs, bool ascii);
 int ds_get_line(struct ds *, FILE *);
-int ds_get_preprocessed_line(struct ds *, FILE *);
+int ds_get_preprocessed_line(struct ds *, FILE *, int *line_number);
 int ds_get_test_line(struct ds *, FILE *);
 
 void ds_put_strftime(struct ds *, const char *template, time_t when, bool utc)
index 1770457..db8d98e 100644 (file)
@@ -42,9 +42,6 @@ VLOG_DEFINE_THIS_MODULE(fatal_signal);
 /* Signals to catch. */
 static const int fatal_signals[] = { SIGTERM, SIGINT, SIGHUP, SIGALRM };
 
-/* Signals to catch as a sigset_t. */
-static sigset_t fatal_signal_set;
-
 /* Hooks to call upon catching a signal */
 struct hook {
     void (*hook_cb)(void *aux);
@@ -75,12 +72,10 @@ fatal_signal_init(void)
 
         xpipe_nonblocking(signal_fds);
 
-        sigemptyset(&fatal_signal_set);
         for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) {
             int sig_nr = fatal_signals[i];
             struct sigaction old_sa;
 
-            sigaddset(&fatal_signal_set, sig_nr);
             xsigaction(sig_nr, NULL, &old_sa);
             if (old_sa.sa_handler == SIG_DFL
                 && signal(sig_nr, fatal_signal_handler) == SIG_ERR) {
index dc0f1b7..fd4199a 100644 (file)
@@ -287,6 +287,19 @@ hindex_node_with_hash(const struct hindex *hindex, size_t hash)
     return node;
 }
 
+/* Returns the head node in 'hindex' with the given 'hash'.  'hindex' must
+ * contain a head node with the given hash. */
+static struct hindex_node *
+hindex_head_node(const struct hindex *hindex, size_t hash)
+{
+    struct hindex_node *node = hindex->buckets[hash & hindex->mask];
+
+    while (node->hash != hash) {
+        node = node->d;
+    }
+    return node;
+}
+
 static struct hindex_node *
 hindex_next__(const struct hindex *hindex, size_t start)
 {
@@ -312,13 +325,23 @@ hindex_first(const struct hindex *hindex)
  * null pointer if 'node' is the last node in 'hindex'.
  *
  * If the hash index has been reallocated since 'node' was visited, some nodes
- * may be skipped or visited twice.  (Removing 'node' from the hash index does
- * not prevent calling this function, since node->next is preserved, although
- * freeing 'node' of course does.) */
+ * may be skipped or visited twice. */
 struct hindex_node *
 hindex_next(const struct hindex *hindex, const struct hindex_node *node)
 {
-    return (node->s ? node->s
-            : node->d && node->d->hash != node->hash ? node->d
-            : hindex_next__(hindex, (node->hash & hindex->mask) + 1));
+    struct hindex_node *head;
+
+    /* If there's a node with the same hash, return it. */
+    if (node->s) {
+        return node->s;
+    }
+
+    /* If there's another node in the same bucket, return it. */
+    head = hindex_head_node(hindex, node->hash);
+    if (head->d) {
+        return head->d;
+    }
+
+    /* Return the first node in the next (or later) bucket. */
+    return hindex_next__(hindex, (node->hash & hindex->mask) + 1);
 }
index 56b4cce..b4bbc84 100644 (file)
@@ -41,6 +41,7 @@ struct jsonrpc {
 
     /* Input. */
     struct byteq input;
+    uint8_t input_buffer[512];
     struct json_parser *parser;
     struct jsonrpc_msg *received;
 
@@ -87,7 +88,7 @@ jsonrpc_open(struct stream *stream)
     rpc = xzalloc(sizeof *rpc);
     rpc->name = xstrdup(stream_get_name(stream));
     rpc->stream = stream;
-    byteq_init(&rpc->input);
+    byteq_init(&rpc->input, rpc->input_buffer, sizeof rpc->input_buffer);
     list_init(&rpc->output);
 
     return rpc;
@@ -330,7 +331,7 @@ jsonrpc_recv(struct jsonrpc *rpc, struct jsonrpc_msg **msgp)
                 jsonrpc_received(rpc);
                 if (rpc->status) {
                     const struct byteq *q = &rpc->input;
-                    if (q->head <= BYTEQ_SIZE) {
+                    if (q->head <= q->size) {
                         stream_report_content(q->buffer, q->head,
                                               STREAM_JSONRPC,
                                               THIS_MODULE, rpc->name);
index 9daca3b..5d90850 100644 (file)
@@ -47,6 +47,7 @@ VLOG_DEFINE_THIS_MODULE(lacp);
 #define LACP_RX_MULTIPLIER 3    /* Multiply by TX rate to get RX rate. */
 
 #define LACP_INFO_LEN 15
+OVS_PACKED(
 struct lacp_info {
     ovs_be16 sys_priority;            /* System priority. */
     uint8_t sys_id[ETH_ADDR_LEN];     /* System ID. */
@@ -54,10 +55,11 @@ struct lacp_info {
     ovs_be16 port_priority;           /* Port priority. */
     ovs_be16 port_id;                 /* Port ID. */
     uint8_t state;                    /* State mask.  See LACP_STATE macros. */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(LACP_INFO_LEN == sizeof(struct lacp_info));
 
 #define LACP_PDU_LEN 110
+OVS_PACKED(
 struct lacp_pdu {
     uint8_t subtype;          /* Always 1. */
     uint8_t version;          /* Always 1. */
@@ -76,7 +78,7 @@ struct lacp_pdu {
     uint8_t collector_len;    /* Always 16. */
     ovs_be16 collector_delay; /* Maximum collector delay. Set to UINT16_MAX. */
     uint8_t z3[64];           /* Combination of several fields.  Always 0. */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(LACP_PDU_LEN == sizeof(struct lacp_pdu));
 \f
 /* Implementation. */
@@ -279,6 +281,10 @@ lacp_process_packet(struct lacp *lacp, const void *slave_,
     const struct lacp_pdu *pdu;
     long long int tx_rate;
 
+    if (!slave) {
+        return;
+    }
+
     pdu = parse_lacp_packet(packet);
     if (!pdu) {
         VLOG_WARN_RL(&rl, "%s: received an unparsable LACP PDU.", lacp->name);
@@ -374,6 +380,10 @@ lacp_slave_carrier_changed(const struct lacp *lacp, const void *slave_)
     if (lacp) {
         struct slave *slave = slave_lookup(lacp, slave_);
 
+        if (!slave) {
+            return;
+        }
+
         if (slave->status == LACP_CURRENT || slave->lacp->active) {
             slave_set_expired(slave);
         }
@@ -395,7 +405,8 @@ bool
 lacp_slave_may_enable(const struct lacp *lacp, const void *slave_)
 {
     if (lacp) {
-        return slave_may_enable__(slave_lookup(lacp, slave_));
+        struct slave *slave = slave_lookup(lacp, slave_);
+        return slave ? slave_may_enable__(slave) : false;
     } else {
         return true;
     }
@@ -407,7 +418,8 @@ lacp_slave_may_enable(const struct lacp *lacp, const void *slave_)
 bool
 lacp_slave_is_current(const struct lacp *lacp, const void *slave_)
 {
-    return slave_lookup(lacp, slave_)->status != LACP_DEFAULTED;
+    struct slave *slave = slave_lookup(lacp, slave_);
+    return slave ? slave->status != LACP_DEFAULTED : false;
 }
 
 /* This function should be called periodically to update 'lacp'. */
diff --git a/lib/latch.c b/lib/latch.c
new file mode 100644 (file)
index 0000000..9b13006
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+
+#include "latch.h"
+#include <errno.h>
+#include <poll.h>
+#include <unistd.h>
+#include "poll-loop.h"
+#include "socket-util.h"
+
+/* Initializes 'latch' as initially unset. */
+void
+latch_init(struct latch *latch)
+{
+    xpipe_nonblocking(latch->fds);
+}
+
+/* Destroys 'latch'. */
+void
+latch_destroy(struct latch *latch)
+{
+    close(latch->fds[0]);
+    close(latch->fds[1]);
+}
+
+/* Resets 'latch' to the unset state.  Returns true if 'latch' was previously
+ * set, false otherwise. */
+bool
+latch_poll(struct latch *latch)
+{
+    char buffer[_POSIX_PIPE_BUF];
+
+    return read(latch->fds[0], buffer, sizeof buffer) > 0;
+}
+
+/* Sets 'latch'.
+ *
+ * Calls are not additive: a single latch_poll() clears out any number of
+ * latch_set(). */
+void
+latch_set(struct latch *latch)
+{
+    ignore(write(latch->fds[1], "", 1));
+}
+
+/* Returns true if 'latch' is set, false otherwise.  Does not reset 'latch'
+ * to the unset state. */
+bool
+latch_is_set(const struct latch *latch)
+{
+    struct pollfd pfd;
+    int retval;
+
+    pfd.fd = latch->fds[0];
+    pfd.events = POLLIN;
+    do {
+        retval = poll(&pfd, 1, 0);
+    } while (retval < 0 && errno == EINTR);
+
+    return pfd.revents & POLLIN;
+}
+
+/* Causes the next poll_block() to wake up when 'latch' is set. */
+void
+(latch_wait)(const struct latch *latch, const char *where)
+{
+    (poll_fd_wait)(latch->fds[0], POLLIN, where);
+}
diff --git a/lib/latch.h b/lib/latch.h
new file mode 100644 (file)
index 0000000..08f45b1
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#ifndef LATCH_H
+#define LATCH_H 1
+
+/* A thread-safe, signal-safe, pollable doorbell.
+ *
+ * This is a thin wrapper around a pipe that allows threads to notify each
+ * other that an event has occurred in a signal-safe way  */
+
+#include <stdbool.h>
+#include "util.h"
+
+struct latch {
+    int fds[2];
+};
+
+void latch_init(struct latch *);
+void latch_destroy(struct latch *);
+
+bool latch_poll(struct latch *);
+void latch_set(struct latch *);
+
+bool latch_is_set(const struct latch *);
+void latch_wait(const struct latch *, const char *where);
+#define latch_wait(latch) latch_wait(latch, SOURCE_LOCATOR)
+
+#endif /* latch.h */
index 0cc562d..49d9efd 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.
@@ -303,6 +303,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->table_id = learn->table_id;
     fm->command = OFPFC_MODIFY_STRICT;
     fm->idle_timeout = learn->idle_timeout;
@@ -390,13 +391,16 @@ learn_mask(const struct ofpact_learn *learn, struct flow_wildcards *wc)
     }
 }
 
-static void
+/* Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec)
 {
     const char *full_s = s;
     const char *arrow = strstr(s, "->");
     struct mf_subfield dst;
     union mf_subvalue imm;
+    char *error;
 
     memset(&imm, 0, sizeof imm);
     if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X') && arrow) {
@@ -408,7 +412,7 @@ learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec)
         for (i = 0; i < n; i++) {
             int hexit = hexit_value(in[-i]);
             if (hexit < 0) {
-                ovs_fatal(0, "%s: bad hex digit in value", full_s);
+                return xasprintf("%s: bad hex digit in value", full_s);
             }
             out[-(i / 2)] |= i % 2 ? hexit << 4 : hexit;
         }
@@ -418,19 +422,19 @@ learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec)
     }
 
     if (strncmp(s, "->", 2)) {
-        ovs_fatal(0, "%s: missing `->' following value", full_s);
+        return xasprintf("%s: missing `->' following value", full_s);
     }
     s += 2;
 
-    s = mf_parse_subfield(&dst, s);
-    if (*s != '\0') {
-        ovs_fatal(0, "%s: trailing garbage following destination", full_s);
+    error = mf_parse_subfield(&dst, s);
+    if (error) {
+        return error;
     }
 
     if (!bitwise_is_all_zeros(&imm, sizeof imm, dst.n_bits,
                               (8 * sizeof imm) - dst.n_bits)) {
-        ovs_fatal(0, "%s: value does not fit into %u bits",
-                  full_s, dst.n_bits);
+        return xasprintf("%s: value does not fit into %u bits",
+                         full_s, dst.n_bits);
     }
 
     spec->n_bits = dst.n_bits;
@@ -438,9 +442,12 @@ learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec)
     spec->src_imm = imm;
     spec->dst_type = NX_LEARN_DST_LOAD;
     spec->dst = dst;
+    return NULL;
 }
 
-static void
+/* Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 learn_parse_spec(const char *orig, char *name, char *value,
                  struct ofpact_learn_spec *spec)
 {
@@ -451,7 +458,7 @@ learn_parse_spec(const char *orig, char *name, char *value,
 
         error = mf_parse_value(dst, value, &imm);
         if (error) {
-            ovs_fatal(0, "%s", error);
+            return error;
         }
 
         spec->n_bits = dst->n_bits;
@@ -465,21 +472,23 @@ learn_parse_spec(const char *orig, char *name, char *value,
         spec->dst.n_bits = dst->n_bits;
     } else if (strchr(name, '[')) {
         /* Parse destination and check prerequisites. */
-        if (mf_parse_subfield(&spec->dst, name)[0] != '\0') {
-            ovs_fatal(0, "%s: syntax error after NXM field name `%s'",
-                      orig, name);
+        char *error;
+
+        error = mf_parse_subfield(&spec->dst, name);
+        if (error) {
+            return error;
         }
 
         /* Parse source and check prerequisites. */
         if (value[0] != '\0') {
-            if (mf_parse_subfield(&spec->src, value)[0] != '\0') {
-                ovs_fatal(0, "%s: syntax error after NXM field name `%s'",
-                          orig, value);
+            error = mf_parse_subfield(&spec->src, value);
+            if (error) {
+                return error;
             }
             if (spec->src.n_bits != spec->dst.n_bits) {
-                ovs_fatal(0, "%s: bit widths of %s (%u) and %s (%u) differ",
-                          orig, name, spec->src.n_bits, value,
-                          spec->dst.n_bits);
+                return xasprintf("%s: bit widths of %s (%u) and %s (%u) "
+                                 "differ", orig, name, spec->src.n_bits, value,
+                                 spec->dst.n_bits);
             }
         } else {
             spec->src = spec->dst;
@@ -490,11 +499,18 @@ learn_parse_spec(const char *orig, char *name, char *value,
         spec->dst_type = NX_LEARN_DST_MATCH;
     } else if (!strcmp(name, "load")) {
         if (value[strcspn(value, "[-")] == '-') {
-            learn_parse_load_immediate(value, spec);
+            char *error = learn_parse_load_immediate(value, spec);
+            if (error) {
+                return error;
+            }
         } else {
             struct ofpact_reg_move move;
+            char *error;
 
-            nxm_parse_reg_move(&move, value);
+            error = nxm_parse_reg_move(&move, value);
+            if (error) {
+                return error;
+            }
 
             spec->n_bits = move.src.n_bits;
             spec->src_type = NX_LEARN_SRC_FIELD;
@@ -503,38 +519,29 @@ learn_parse_spec(const char *orig, char *name, char *value,
             spec->dst = move.dst;
         }
     } else if (!strcmp(name, "output")) {
-        if (mf_parse_subfield(&spec->src, value)[0] != '\0') {
-            ovs_fatal(0, "%s: syntax error after NXM field name `%s'",
-                      orig, name);
+        char *error = mf_parse_subfield(&spec->src, value);
+        if (error) {
+            return error;
         }
 
         spec->n_bits = spec->src.n_bits;
         spec->src_type = NX_LEARN_SRC_FIELD;
         spec->dst_type = NX_LEARN_DST_OUTPUT;
     } else {
-        ovs_fatal(0, "%s: unknown keyword %s", orig, name);
+        return xasprintf("%s: unknown keyword %s", orig, name);
     }
+
+    return NULL;
 }
 
-/* Parses 'arg' as a set of arguments to the "learn" action and appends a
- * matching OFPACT_LEARN action to 'ofpacts'.  ovs-ofctl(8) describes the
- * format parsed.
- *
- * Prints an error on stderr and aborts the program if 'arg' syntax is invalid.
- *
- * If 'flow' is nonnull, then it should be the flow from a struct match that is
- * the matching rule for the learning action.  This helps to better validate
- * the action's arguments.
- *
- * Modifies 'arg'. */
-void
-learn_parse(char *arg, struct ofpbuf *ofpacts)
+/* Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+learn_parse__(char *orig, char *arg, struct ofpbuf *ofpacts)
 {
-    char *orig = xstrdup(arg);
-    char *name, *value;
-
     struct ofpact_learn *learn;
     struct match match;
+    char *name, *value;
 
     learn = ofpact_put_LEARN(ofpacts);
     learn->idle_timeout = OFP_FLOW_PERMANENT;
@@ -547,8 +554,8 @@ learn_parse(char *arg, struct ofpbuf *ofpacts)
         if (!strcmp(name, "table")) {
             learn->table_id = atoi(value);
             if (learn->table_id == 255) {
-                ovs_fatal(0, "%s: table id 255 not valid for `learn' action",
-                          orig);
+                return xasprintf("%s: table id 255 not valid for `learn' "
+                                 "action", orig);
             }
         } else if (!strcmp(name, "priority")) {
             learn->priority = atoi(value);
@@ -564,12 +571,16 @@ learn_parse(char *arg, struct ofpbuf *ofpacts)
             learn->cookie = strtoull(value, NULL, 0);
         } else {
             struct ofpact_learn_spec *spec;
+            char *error;
 
             spec = ofpbuf_put_zeros(ofpacts, sizeof *spec);
             learn = ofpacts->l2;
             learn->n_specs++;
 
-            learn_parse_spec(orig, name, value, spec);
+            error = learn_parse_spec(orig, name, value, spec);
+            if (error) {
+                return error;
+            }
 
             /* Update 'match' to allow for satisfying destination
              * prerequisites. */
@@ -581,7 +592,28 @@ learn_parse(char *arg, struct ofpbuf *ofpacts)
     }
     ofpact_update_len(ofpacts, &learn->ofpact);
 
+    return NULL;
+}
+
+/* Parses 'arg' as a set of arguments to the "learn" action and appends a
+ * matching OFPACT_LEARN action to 'ofpacts'.  ovs-ofctl(8) describes the
+ * format parsed.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string.
+ *
+ * If 'flow' is nonnull, then it should be the flow from a struct match that is
+ * the matching rule for the learning action.  This helps to better validate
+ * the action's arguments.
+ *
+ * Modifies 'arg'. */
+char * WARN_UNUSED_RESULT
+learn_parse(char *arg, struct ofpbuf *ofpacts)
+{
+    char *orig = xstrdup(arg);
+    char *error = learn_parse__(orig, arg, ofpacts);
     free(orig);
+    return error;
 }
 
 /* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8)
index 5bceb6e..0e676fe 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.
@@ -17,6 +17,7 @@
 #ifndef LEARN_H
 #define LEARN_H 1
 
+#include "compiler.h"
 #include "ofp-errors.h"
 
 struct ds;
@@ -41,7 +42,7 @@ void learn_execute(const struct ofpact_learn *, const struct flow *,
                    struct ofputil_flow_mod *, struct ofpbuf *ofpacts);
 void learn_mask(const struct ofpact_learn *, struct flow_wildcards *);
 
-void learn_parse(char *, struct ofpbuf *ofpacts);
+char *learn_parse(char *, struct ofpbuf *ofpacts) WARN_UNUSED_RESULT;
 void learn_format(const struct ofpact_learn *, struct ds *);
 
 #endif /* learn.h */
index 9e02860..872e58d 100644 (file)
@@ -378,20 +378,20 @@ lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg)
     case OFPTYPE_GET_ASYNC_REPLY:
     case OFPTYPE_SET_ASYNC_CONFIG:
     case OFPTYPE_METER_MOD:
-    case OFPTYPE_GROUP_REQUEST:
-    case OFPTYPE_GROUP_REPLY:
-    case OFPTYPE_GROUP_DESC_REQUEST:
-    case OFPTYPE_GROUP_DESC_REPLY:
-    case OFPTYPE_GROUP_FEATURES_REQUEST:
-    case OFPTYPE_GROUP_FEATURES_REPLY:
-    case OFPTYPE_METER_REQUEST:
-    case OFPTYPE_METER_REPLY:
-    case OFPTYPE_METER_CONFIG_REQUEST:
-    case OFPTYPE_METER_CONFIG_REPLY:
-    case OFPTYPE_METER_FEATURES_REQUEST:
-    case OFPTYPE_METER_FEATURES_REPLY:
-    case OFPTYPE_TABLE_FEATURES_REQUEST:
-    case OFPTYPE_TABLE_FEATURES_REPLY:
+    case OFPTYPE_GROUP_STATS_REQUEST:
+    case OFPTYPE_GROUP_STATS_REPLY:
+    case OFPTYPE_GROUP_DESC_STATS_REQUEST:
+    case OFPTYPE_GROUP_DESC_STATS_REPLY:
+    case OFPTYPE_GROUP_FEATURES_STATS_REQUEST:
+    case OFPTYPE_GROUP_FEATURES_STATS_REPLY:
+    case OFPTYPE_METER_STATS_REQUEST:
+    case OFPTYPE_METER_STATS_REPLY:
+    case OFPTYPE_METER_CONFIG_STATS_REQUEST:
+    case OFPTYPE_METER_CONFIG_STATS_REPLY:
+    case OFPTYPE_METER_FEATURES_STATS_REQUEST:
+    case OFPTYPE_METER_FEATURES_STATS_REPLY:
+    case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
+    case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
     default:
         if (VLOG_IS_DBG_ENABLED()) {
             char *s = ofp_to_string(msg->data, msg->size, 2);
index 6d66eba..91c05a7 100644 (file)
@@ -111,21 +111,22 @@ match_wc_init(struct match *match, const struct flow *flow)
     if (is_ip_any(flow)) {
         memset(&wc->masks.nw_tos, 0xff, sizeof wc->masks.nw_tos);
         memset(&wc->masks.nw_ttl, 0xff, sizeof wc->masks.nw_ttl);
-    }
 
-    if (flow->nw_frag) {
-        memset(&wc->masks.nw_frag, 0xff, sizeof wc->masks.nw_frag);
-    }
+        if (flow->nw_frag) {
+            memset(&wc->masks.nw_frag, 0xff, sizeof wc->masks.nw_frag);
+        }
 
-    if (flow->nw_proto == IPPROTO_ICMP || flow->nw_proto == IPPROTO_ICMPV6 ||
-        (flow->tp_src || flow->tp_dst)) {
-        memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
-        memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
-    }
+        if (flow->nw_proto == IPPROTO_ICMP ||
+            flow->nw_proto == IPPROTO_ICMPV6 ||
+            (flow->tp_src || flow->tp_dst)) {
+            memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
+            memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
+        }
 
-    if (flow->nw_proto == IPPROTO_ICMPV6) {
-        memset(&wc->masks.arp_sha, 0xff, sizeof wc->masks.arp_sha);
-        memset(&wc->masks.arp_tha, 0xff, sizeof wc->masks.arp_tha);
+        if (flow->nw_proto == IPPROTO_ICMPV6) {
+            memset(&wc->masks.arp_sha, 0xff, sizeof wc->masks.arp_sha);
+            memset(&wc->masks.arp_tha, 0xff, sizeof wc->masks.arp_tha);
+        }
     }
 
     return;
index 0677202..6b3b5eb 100644 (file)
@@ -2641,7 +2641,7 @@ mf_parse_subfield_name(const char *name, int name_len, bool *wild)
  * bit indexes.  "..end" may be omitted to indicate a single bit.  "start..end"
  * may both be omitted (the [] are still required) to indicate an entire
  * field. */
-char *
+char * WARN_UNUSED_RESULT
 mf_parse_subfield__(struct mf_subfield *sf, const char **sp)
 {
     const struct mf_field *field;
@@ -2696,24 +2696,23 @@ mf_parse_subfield__(struct mf_subfield *sf, const char **sp)
     return NULL;
 }
 
-/* Parses a subfield from the beginning of 's' into 'sf'.  Returns the first
- * byte in 's' following the parsed string.
- *
- * Exits with an error message if 's' has incorrect syntax.
+/* Parses a subfield from the entirety of 's' into 'sf'.  Returns NULL if
+ * successful, otherwise a malloc()'d string describing the error.  The caller
+ * is responsible for freeing the returned string.
  *
  * The syntax parsed from 's' takes the form "header[start..end]" where
  * 'header' is the name of an NXM field and 'start' and 'end' are (inclusive)
  * bit indexes.  "..end" may be omitted to indicate a single bit.  "start..end"
  * may both be omitted (the [] are still required) to indicate an entire
  * field.  */
-const char *
+char * WARN_UNUSED_RESULT
 mf_parse_subfield(struct mf_subfield *sf, const char *s)
 {
-    char *msg = mf_parse_subfield__(sf, &s);
-    if (msg) {
-        ovs_fatal(0, "%s", msg);
+    char *error = mf_parse_subfield__(sf, &s);
+    if (!error && s[0]) {
+        error = xstrdup("unexpected input following field syntax");
     }
-    return s;
+    return error;
 }
 
 void
index a85a193..bc402dc 100644 (file)
@@ -360,8 +360,10 @@ uint64_t mf_get_subfield(const struct mf_subfield *, const struct flow *);
 
 
 void mf_format_subfield(const struct mf_subfield *, struct ds *);
-char *mf_parse_subfield__(struct mf_subfield *sf, const char **s);
-const char *mf_parse_subfield(struct mf_subfield *, const char *);
+char *mf_parse_subfield__(struct mf_subfield *sf, const char **s)
+    WARN_UNUSED_RESULT;
+char *mf_parse_subfield(struct mf_subfield *, const char *s)
+    WARN_UNUSED_RESULT;
 
 enum ofperr mf_check_src(const struct mf_subfield *, const struct flow *);
 enum ofperr mf_check_dst(const struct mf_subfield *, const struct flow *);
index 1be6964..6c0560d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 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.
@@ -195,14 +195,14 @@ multipath_algorithm(uint32_t hash, enum nx_mp_algorithm algorithm,
 /* Parses 's_' as a set of arguments to the "multipath" action and initializes
  * 'mp' accordingly.  ovs-ofctl(8) describes the format parsed.
  *
- * Prints an error on stderr and aborts the program if 's_' syntax is
- * invalid. */
-void
-multipath_parse(struct ofpact_multipath *mp, const char *s_)
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string.*/
+static char * WARN_UNUSED_RESULT
+multipath_parse__(struct ofpact_multipath *mp, const char *s_, char *s)
 {
-    char *s = xstrdup(s_);
     char *save_ptr = NULL;
     char *fields, *basis, *algorithm, *n_links_str, *arg, *dst;
+    char *error;
     int n_links;
 
     fields = strtok_r(s, ", ", &save_ptr);
@@ -212,7 +212,7 @@ multipath_parse(struct ofpact_multipath *mp, const char *s_)
     arg = strtok_r(NULL, ", ", &save_ptr);
     dst = strtok_r(NULL, ", ", &save_ptr);
     if (!dst) {
-        ovs_fatal(0, "%s: not enough arguments to multipath action", s_);
+        return xasprintf("%s: not enough arguments to multipath action", s_);
     }
 
     ofpact_init_MULTIPATH(mp);
@@ -221,7 +221,7 @@ multipath_parse(struct ofpact_multipath *mp, const char *s_)
     } else if (!strcasecmp(fields, "symmetric_l4")) {
         mp->fields = NX_HASH_FIELDS_SYMMETRIC_L4;
     } else {
-        ovs_fatal(0, "%s: unknown fields `%s'", s_, fields);
+        return xasprintf("%s: unknown fields `%s'", s_, fields);
     }
     mp->basis = atoi(basis);
     if (!strcasecmp(algorithm, "modulo_n")) {
@@ -233,24 +233,41 @@ multipath_parse(struct ofpact_multipath *mp, const char *s_)
     } else if (!strcasecmp(algorithm, "iter_hash")) {
         mp->algorithm = NX_MP_ALG_ITER_HASH;
     } else {
-        ovs_fatal(0, "%s: unknown algorithm `%s'", s_, algorithm);
+        return xasprintf("%s: unknown algorithm `%s'", s_, algorithm);
     }
     n_links = atoi(n_links_str);
     if (n_links < 1 || n_links > 65536) {
-        ovs_fatal(0, "%s: n_links %d is not in valid range 1 to 65536",
-                  s_, n_links);
+        return xasprintf("%s: n_links %d is not in valid range 1 to 65536",
+                         s_, n_links);
     }
     mp->max_link = n_links - 1;
     mp->arg = atoi(arg);
 
-    mf_parse_subfield(&mp->dst, dst);
+    error = mf_parse_subfield(&mp->dst, dst);
+    if (error) {
+        return error;
+    }
     if (mp->dst.n_bits < 16 && n_links > (1u << mp->dst.n_bits)) {
-        ovs_fatal(0, "%s: %d-bit destination field has %u possible values, "
-                  "less than specified n_links %d",
-                  s_, mp->dst.n_bits, 1u << mp->dst.n_bits, n_links);
+        return xasprintf("%s: %d-bit destination field has %u possible "
+                         "values, less than specified n_links %d",
+                         s_, mp->dst.n_bits, 1u << mp->dst.n_bits, n_links);
     }
 
+    return NULL;
+}
+
+/* Parses 's_' as a set of arguments to the "multipath" action and initializes
+ * 'mp' accordingly.  ovs-ofctl(8) describes the format parsed.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+char * WARN_UNUSED_RESULT
+multipath_parse(struct ofpact_multipath *mp, const char *s_)
+{
+    char *s = xstrdup(s_);
+    char *error = multipath_parse__(mp, s_, s);
     free(s);
+    return error;
 }
 
 /* Appends a description of 'mp' to 's', in the format that ovs-ofctl(8)
index 97b5161..0673050 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 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.
@@ -18,6 +18,7 @@
 #define MULTIPATH_H 1
 
 #include <stdint.h>
+#include "compiler.h"
 #include "ofp-errors.h"
 
 struct ds;
@@ -42,7 +43,8 @@ void multipath_to_nxast(const struct ofpact_multipath *,
 void multipath_execute(const struct ofpact_multipath *, struct flow *,
                        struct flow_wildcards *);
 
-void multipath_parse(struct ofpact_multipath *, const char *);
+char *multipath_parse(struct ofpact_multipath *, const char *)
+    WARN_UNUSED_RESULT;
 void multipath_format(const struct ofpact_multipath *, struct ds *);
 
 #endif /* multipath.h */
index 8790f14..05877c1 100644 (file)
@@ -409,9 +409,6 @@ static const struct netdev_rx_class netdev_rx_linux_class;
 /* Sockets used for ioctl operations. */
 static int af_inet_sock = -1;   /* AF_INET, SOCK_DGRAM. */
 
-/* A Netlink routing socket that is not subscribed to any multicast groups. */
-static struct nl_sock *rtnl_sock;
-
 /* This is set pretty low because we probably won't learn anything from the
  * additional log messages. */
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
@@ -477,15 +474,6 @@ netdev_linux_init(void)
         if (status) {
             VLOG_ERR("failed to create inet socket: %s", ovs_strerror(status));
         }
-
-        /* Create rtnetlink socket. */
-        if (!status) {
-            status = nl_sock_create(NETLINK_ROUTE, &rtnl_sock);
-            if (status) {
-                VLOG_ERR_RL(&rl, "failed to create rtnetlink socket: %s",
-                            ovs_strerror(status));
-            }
-        }
     }
     return status;
 }
@@ -835,10 +823,8 @@ netdev_rx_linux_recv(struct netdev_rx *rx_, void *data, size_t size)
                   : recv(rx->fd, data, size, MSG_TRUNC));
     } while (retval < 0 && errno == EINTR);
 
-    if (retval > size) {
-        return -EMSGSIZE;
-    } else if (retval >= 0) {
-        return retval;
+    if (retval >= 0) {
+        return retval > size ? -EMSGSIZE : retval;
     } else {
         if (errno != EAGAIN) {
             VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
@@ -2027,7 +2013,7 @@ start_queue_dump(const struct netdev *netdev, struct nl_dump *dump)
         return false;
     }
     tcmsg->tcm_parent = 0;
-    nl_dump_start(dump, rtnl_sock, &request);
+    nl_dump_start(dump, NETLINK_ROUTE, &request);
     ofpbuf_uninit(&request);
     return true;
 }
@@ -3646,7 +3632,7 @@ tc_make_request(const struct netdev *netdev, int type, unsigned int flags,
 static int
 tc_transact(struct ofpbuf *request, struct ofpbuf **replyp)
 {
-    int error = nl_sock_transact(rtnl_sock, request, replyp);
+    int error = nl_transact(NETLINK_ROUTE, request, replyp);
     ofpbuf_uninit(request);
     return error;
 }
@@ -4322,7 +4308,7 @@ get_stats_via_netlink(int ifindex, struct netdev_stats *stats)
     ifi = ofpbuf_put_zeros(&request, sizeof *ifi);
     ifi->ifi_family = PF_UNSPEC;
     ifi->ifi_index = ifindex;
-    error = nl_sock_transact(rtnl_sock, &request, &reply);
+    error = nl_transact(NETLINK_ROUTE, &request, &reply);
     ofpbuf_uninit(&request);
     if (error) {
         return error;
index dfe39ac..da32284 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 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.
@@ -29,9 +29,9 @@
 #include "netlink.h"
 #include "netlink-protocol.h"
 #include "ofpbuf.h"
+#include "ovs-thread.h"
 #include "poll-loop.h"
 #include "socket-util.h"
-#include "stress.h"
 #include "util.h"
 #include "vlog.h"
 
@@ -64,7 +64,6 @@ struct nl_sock {
     uint32_t next_seq;
     uint32_t pid;
     int protocol;
-    struct nl_dump *dump;
     unsigned int rcvbuf;        /* Receive buffer size (SO_RCVBUF). */
 };
 
@@ -78,21 +77,23 @@ struct nl_sock {
  * Initialized by nl_sock_create(). */
 static int max_iovs;
 
-static int nl_sock_cow__(struct nl_sock *);
+static int nl_pool_alloc(int protocol, struct nl_sock **sockp);
+static void nl_pool_release(struct nl_sock *);
 
 /* Creates a new netlink socket for the given netlink 'protocol'
  * (NETLINK_ROUTE, NETLINK_GENERIC, ...).  Returns 0 and sets '*sockp' to the
- * new socket if successful, otherwise returns a positive errno value.  */
+ * new socket if successful, otherwise returns a positive errno value. */
 int
 nl_sock_create(int protocol, struct nl_sock **sockp)
 {
+    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
     struct nl_sock *sock;
     struct sockaddr_nl local, remote;
     socklen_t local_size;
     int rcvbuf;
     int retval = 0;
 
-    if (!max_iovs) {
+    if (ovsthread_once_start(&once)) {
         int save_errno = errno;
         errno = 0;
 
@@ -107,6 +108,7 @@ nl_sock_create(int protocol, struct nl_sock **sockp)
         }
 
         errno = save_errno;
+        ovsthread_once_done(&once);
     }
 
     *sockp = NULL;
@@ -118,7 +120,6 @@ nl_sock_create(int protocol, struct nl_sock **sockp)
         goto error;
     }
     sock->protocol = protocol;
-    sock->dump = NULL;
     sock->next_seq = 1;
 
     rcvbuf = 1024 * 1024;
@@ -192,12 +193,8 @@ void
 nl_sock_destroy(struct nl_sock *sock)
 {
     if (sock) {
-        if (sock->dump) {
-            sock->dump = NULL;
-        } else {
-            close(sock->fd);
-            free(sock);
-        }
+        close(sock->fd);
+        free(sock);
     }
 }
 
@@ -215,10 +212,6 @@ nl_sock_destroy(struct nl_sock *sock)
 int
 nl_sock_join_mcgroup(struct nl_sock *sock, unsigned int multicast_group)
 {
-    int error = nl_sock_cow__(sock);
-    if (error) {
-        return error;
-    }
     if (setsockopt(sock->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
                    &multicast_group, sizeof multicast_group) < 0) {
         VLOG_WARN("could not join multicast group %u (%s)",
@@ -241,7 +234,6 @@ nl_sock_join_mcgroup(struct nl_sock *sock, unsigned int multicast_group)
 int
 nl_sock_leave_mcgroup(struct nl_sock *sock, unsigned int multicast_group)
 {
-    ovs_assert(!sock->dump);
     if (setsockopt(sock->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
                    &multicast_group, sizeof multicast_group) < 0) {
         VLOG_WARN("could not leave multicast group %u (%s)",
@@ -302,22 +294,9 @@ int
 nl_sock_send_seq(struct nl_sock *sock, const struct ofpbuf *msg,
                  uint32_t nlmsg_seq, bool wait)
 {
-    int error = nl_sock_cow__(sock);
-    if (error) {
-        return error;
-    }
     return nl_sock_send__(sock, msg, nlmsg_seq, wait);
 }
 
-/* This stress option is useful for testing that OVS properly tolerates
- * -ENOBUFS on NetLink sockets.  Such errors are unavoidable because they can
- * occur if the kernel cannot temporarily allocate enough GFP_ATOMIC memory to
- * reply to a request.  They can also occur if messages arrive on a multicast
- * channel faster than OVS can process them. */
-STRESS_OPTION(
-    netlink_overflow, "simulate netlink socket receive buffer overflow",
-    5, 1, -1, 100);
-
 static int
 nl_sock_recv__(struct nl_sock *sock, struct ofpbuf *buf, bool wait)
 {
@@ -373,10 +352,6 @@ nl_sock_recv__(struct nl_sock *sock, struct ofpbuf *buf, bool wait)
         return EPROTO;
     }
 
-    if (STRESS(netlink_overflow)) {
-        return ENOBUFS;
-    }
-
     buf->size = MIN(retval, buf->allocated);
     if (retval > buf->allocated) {
         COVERAGE_INC(netlink_recv_jumbo);
@@ -409,10 +384,6 @@ nl_sock_recv__(struct nl_sock *sock, struct ofpbuf *buf, bool wait)
 int
 nl_sock_recv(struct nl_sock *sock, struct ofpbuf *buf, bool wait)
 {
-    int error = nl_sock_cow__(sock);
-    if (error) {
-        return error;
-    }
     return nl_sock_recv__(sock, buf, wait);
 }
 
@@ -585,12 +556,6 @@ nl_sock_transact_multiple(struct nl_sock *sock,
         return;
     }
 
-    error = nl_sock_cow__(sock);
-    if (error) {
-        nl_sock_record_errors__(transactions, n, error);
-        return;
-    }
-
     /* In theory, every request could have a 64 kB reply.  But the default and
      * maximum socket rcvbuf size with typical Dom0 memory sizes both tend to
      * be a bit below 128 kB, so that would only allow a single message in a
@@ -704,93 +669,36 @@ nl_sock_transact(struct nl_sock *sock, const struct ofpbuf *request,
 int
 nl_sock_drain(struct nl_sock *sock)
 {
-    int error = nl_sock_cow__(sock);
-    if (error) {
-        return error;
-    }
     return drain_rcvbuf(sock->fd);
 }
 
-/* The client is attempting some operation on 'sock'.  If 'sock' has an ongoing
- * dump operation, then replace 'sock''s fd with a new socket and hand 'sock''s
- * old fd over to the dump. */
-static int
-nl_sock_cow__(struct nl_sock *sock)
-{
-    struct nl_sock *copy;
-    uint32_t tmp_pid;
-    int tmp_fd;
-    int error;
-
-    if (!sock->dump) {
-        return 0;
-    }
-
-    error = nl_sock_clone(sock, &copy);
-    if (error) {
-        return error;
-    }
-
-    tmp_fd = sock->fd;
-    sock->fd = copy->fd;
-    copy->fd = tmp_fd;
-
-    tmp_pid = sock->pid;
-    sock->pid = copy->pid;
-    copy->pid = tmp_pid;
-
-    sock->dump->sock = copy;
-    sock->dump = NULL;
-
-    return 0;
-}
-
-/* Starts a Netlink "dump" operation, by sending 'request' to the kernel via
- * 'sock', and initializes 'dump' to reflect the state of the operation.
+/* Starts a Netlink "dump" operation, by sending 'request' to the kernel on a
+ * Netlink socket created with the given 'protocol', and initializes 'dump' to
+ * reflect the state of the operation.
  *
  * nlmsg_len in 'msg' will be finalized to match msg->size, and nlmsg_pid will
- * be set to 'sock''s pid, before the message is sent.  NLM_F_DUMP and
- * NLM_F_ACK will be set in nlmsg_flags.
- *
- * This Netlink socket library is designed to ensure that the dump is reliable
- * and that it will not interfere with other operations on 'sock', including
- * destroying or sending and receiving messages on 'sock'.  One corner case is
- * not handled:
+ * be set to the Netlink socket's pid, before the message is sent.  NLM_F_DUMP
+ * and NLM_F_ACK will be set in nlmsg_flags.
  *
- *   - If 'sock' has been used to send a request (e.g. with nl_sock_send())
- *     whose response has not yet been received (e.g. with nl_sock_recv()).
- *     This is unusual: usually nl_sock_transact() is used to send a message
- *     and receive its reply all in one go.
+ * The design of this Netlink socket library ensures that the dump is reliable.
  *
  * This function provides no status indication.  An error status for the entire
  * dump operation is provided when it is completed by calling nl_dump_done().
  *
  * The caller is responsible for destroying 'request'.
- *
- * The new 'dump' is independent of 'sock'.  'sock' and 'dump' may be destroyed
- * in either order.
  */
 void
-nl_dump_start(struct nl_dump *dump,
-              struct nl_sock *sock, const struct ofpbuf *request)
+nl_dump_start(struct nl_dump *dump, int protocol, const struct ofpbuf *request)
 {
     ofpbuf_init(&dump->buffer, 4096);
-    if (sock->dump) {
-        /* 'sock' already has an ongoing dump.  Clone the socket because
-         * Netlink only allows one dump at a time. */
-        dump->status = nl_sock_clone(sock, &dump->sock);
-        if (dump->status) {
-            return;
-        }
-    } else {
-        sock->dump = dump;
-        dump->sock = sock;
-        dump->status = 0;
+    dump->status = nl_pool_alloc(protocol, &dump->sock);
+    if (dump->status) {
+        return;
     }
 
     nl_msg_nlmsghdr(request)->nlmsg_flags |= NLM_F_DUMP | NLM_F_ACK;
-    dump->status = nl_sock_send__(sock, request, nl_sock_allocate_seq(sock, 1),
-                                  true);
+    dump->status = nl_sock_send__(dump->sock, request,
+                                  nl_sock_allocate_seq(dump->sock, 1), true);
     dump->seq = nl_msg_nlmsghdr(request)->nlmsg_seq;
 }
 
@@ -876,21 +784,16 @@ int
 nl_dump_done(struct nl_dump *dump)
 {
     /* Drain any remaining messages that the client didn't read.  Otherwise the
-     * kernel will continue to queue them up and waste buffer space. */
+     * kernel will continue to queue them up and waste buffer space.
+     *
+     * XXX We could just destroy and discard the socket in this case. */
     while (!dump->status) {
         struct ofpbuf reply;
         if (!nl_dump_next(dump, &reply)) {
             ovs_assert(dump->status);
         }
     }
-
-    if (dump->sock) {
-        if (dump->sock->dump) {
-            dump->sock->dump = NULL;
-        } else {
-            nl_sock_destroy(dump->sock);
-        }
-    }
+    nl_pool_release(dump->sock);
     ofpbuf_uninit(&dump->buffer);
     return dump->status == EOF ? 0 : dump->status;
 }
@@ -1103,6 +1006,90 @@ nl_lookup_genl_family(const char *name, int *number)
     }
     return *number > 0 ? 0 : -*number;
 }
+\f
+struct nl_pool {
+    struct nl_sock *socks[16];
+    int n;
+};
+
+static struct nl_pool pools[MAX_LINKS];
+static pthread_mutex_t pool_mutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER;
+
+static int
+nl_pool_alloc(int protocol, struct nl_sock **sockp)
+{
+    struct nl_sock *sock = NULL;
+    struct nl_pool *pool;
+
+    ovs_assert(protocol >= 0 && protocol < ARRAY_SIZE(pools));
+
+    xpthread_mutex_lock(&pool_mutex);
+    pool = &pools[protocol];
+    if (pool->n > 0) {
+        sock = pool->socks[--pool->n];
+    }
+    xpthread_mutex_unlock(&pool_mutex);
+
+    if (sock) {
+        *sockp = sock;
+        return 0;
+    } else {
+        return nl_sock_create(protocol, sockp);
+    }
+}
+
+static void
+nl_pool_release(struct nl_sock *sock)
+{
+    if (sock) {
+        struct nl_pool *pool = &pools[sock->protocol];
+
+        xpthread_mutex_lock(&pool_mutex);
+        if (pool->n < ARRAY_SIZE(pool->socks)) {
+            pool->socks[pool->n++] = sock;
+            sock = NULL;
+        }
+        xpthread_mutex_unlock(&pool_mutex);
+
+        nl_sock_destroy(sock);
+    }
+}
+
+int
+nl_transact(int protocol, const struct ofpbuf *request,
+            struct ofpbuf **replyp)
+{
+    struct nl_sock *sock;
+    int error;
+
+    error = nl_pool_alloc(protocol, &sock);
+    if (error) {
+        *replyp = NULL;
+        return error;
+    }
+
+    error = nl_sock_transact(sock, request, replyp);
+
+    nl_pool_release(sock);
+    return error;
+}
+
+void
+nl_transact_multiple(int protocol,
+                     struct nl_transaction **transactions, size_t n)
+{
+    struct nl_sock *sock;
+    int error;
+
+    error = nl_pool_alloc(protocol, &sock);
+    if (!error) {
+        nl_sock_transact_multiple(sock, transactions, n);
+        nl_pool_release(sock);
+    } else {
+        nl_sock_record_errors__(transactions, n, error);
+    }
+}
+
 \f
 static uint32_t
 nl_sock_allocate_seq(struct nl_sock *sock, unsigned int n)
index 78dd7b2..986b574 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 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.
  * are Linux-specific.  For Netlink protocol definitions, see
  * netlink-protocol.h.  For helper functions for working with Netlink messages,
  * see netlink.h.
+ *
+ *
+ * Thread-safety
+ * =============
+ *
+ * Only a single thread may use a given nl_sock or nl_dump at one time.
  */
 
 #include <stdbool.h>
@@ -84,6 +90,11 @@ struct nl_transaction {
 void nl_sock_transact_multiple(struct nl_sock *,
                                struct nl_transaction **, size_t n);
 
+/* Transactions without an allocated socket. */
+int nl_transact(int protocol, const struct ofpbuf *request,
+                struct ofpbuf **replyp);
+void nl_transact_multiple(int protocol, struct nl_transaction **, size_t n);
+
 /* Table dumping. */
 struct nl_dump {
     struct nl_sock *sock;       /* Socket being dumped. */
@@ -92,7 +103,7 @@ struct nl_dump {
     int status;                 /* 0=OK, EOF=done, or positive errno value. */
 };
 
-void nl_dump_start(struct nl_dump *, struct nl_sock *,
+void nl_dump_start(struct nl_dump *, int protocol,
                    const struct ofpbuf *request);
 bool nl_dump_next(struct nl_dump *, struct ofpbuf *reply);
 int nl_dump_done(struct nl_dump *);
index 7e7884e..a686784 100644 (file)
@@ -607,17 +607,41 @@ nl_attr_get_nested(const struct nlattr *nla, struct ofpbuf *nested)
     ofpbuf_use_const(nested, nl_attr_get(nla), nl_attr_get_size(nla));
 }
 
-/* Default minimum and maximum payload sizes for each type of attribute. */
-static const size_t attr_len_range[][2] = {
-    [0 ... N_NL_ATTR_TYPES - 1] = { 0, SIZE_MAX },
-    [NL_A_U8] = { 1, 1 },
-    [NL_A_U16] = { 2, 2 },
-    [NL_A_U32] = { 4, 4 },
-    [NL_A_U64] = { 8, 8 },
-    [NL_A_STRING] = { 1, SIZE_MAX },
-    [NL_A_FLAG] = { 0, SIZE_MAX },
-    [NL_A_NESTED] = { 0, SIZE_MAX },
-};
+/* Default minimum payload size for each type of attribute. */
+static size_t
+min_attr_len(enum nl_attr_type type)
+{
+    switch (type) {
+    case NL_A_NO_ATTR: return 0;
+    case NL_A_UNSPEC: return 0;
+    case NL_A_U8: return 1;
+    case NL_A_U16: return 2;
+    case NL_A_U32: return 4;
+    case NL_A_U64: return 8;
+    case NL_A_STRING: return 1;
+    case NL_A_FLAG: return 0;
+    case NL_A_NESTED: return 0;
+    case N_NL_ATTR_TYPES: default: NOT_REACHED();
+    }
+}
+
+/* Default maximum payload size for each type of attribute. */
+static size_t
+max_attr_len(enum nl_attr_type type)
+{
+    switch (type) {
+    case NL_A_NO_ATTR: return SIZE_MAX;
+    case NL_A_UNSPEC: return SIZE_MAX;
+    case NL_A_U8: return 1;
+    case NL_A_U16: return 2;
+    case NL_A_U32: return 4;
+    case NL_A_U64: return 8;
+    case NL_A_STRING: return SIZE_MAX;
+    case NL_A_FLAG: return SIZE_MAX;
+    case NL_A_NESTED: return SIZE_MAX;
+    case N_NL_ATTR_TYPES: default: NOT_REACHED();
+    }
+}
 
 bool
 nl_attr_validate(const struct nlattr *nla, const struct nl_policy *policy)
@@ -634,11 +658,11 @@ nl_attr_validate(const struct nlattr *nla, const struct nl_policy *policy)
     /* Figure out min and max length. */
     min_len = policy->min_len;
     if (!min_len) {
-        min_len = attr_len_range[policy->type][0];
+        min_len = min_attr_len(policy->type);
     }
     max_len = policy->max_len;
     if (!max_len) {
-        max_len = attr_len_range[policy->type][1];
+        max_len = max_attr_len(policy->type);
     }
 
     /* Verify length. */
index 8bdd8ec..3a6d7cc 100644 (file)
@@ -1004,50 +1004,67 @@ oxm_match_from_string(const char *s, struct ofpbuf *b)
     return match_len;
 }
 \f
-void
+/* Parses 's' as a "move" action, in the form described in ovs-ofctl(8), into
+ * '*move'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+char * WARN_UNUSED_RESULT
 nxm_parse_reg_move(struct ofpact_reg_move *move, const char *s)
 {
     const char *full_s = s;
+    char *error;
 
-    s = mf_parse_subfield(&move->src, s);
+    error = mf_parse_subfield__(&move->src, &s);
+    if (error) {
+        return error;
+    }
     if (strncmp(s, "->", 2)) {
-        ovs_fatal(0, "%s: missing `->' following source", full_s);
+        return xasprintf("%s: missing `->' following source", full_s);
     }
     s += 2;
-    s = mf_parse_subfield(&move->dst, s);
-    if (*s != '\0') {
-        ovs_fatal(0, "%s: trailing garbage following destination", full_s);
+    error = mf_parse_subfield(&move->dst, s);
+    if (error) {
+        return error;
     }
 
     if (move->src.n_bits != move->dst.n_bits) {
-        ovs_fatal(0, "%s: source field is %d bits wide but destination is "
-                  "%d bits wide", full_s,
-                  move->src.n_bits, move->dst.n_bits);
+        return xasprintf("%s: source field is %d bits wide but destination is "
+                         "%d bits wide", full_s,
+                         move->src.n_bits, move->dst.n_bits);
     }
+    return NULL;
 }
 
-void
+/* Parses 's' as a "load" action, in the form described in ovs-ofctl(8), into
+ * '*load'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+char * WARN_UNUSED_RESULT
 nxm_parse_reg_load(struct ofpact_reg_load *load, const char *s)
 {
     const char *full_s = s;
     uint64_t value = strtoull(s, (char **) &s, 0);
+    char *error;
 
     if (strncmp(s, "->", 2)) {
-        ovs_fatal(0, "%s: missing `->' following value", full_s);
+        return xasprintf("%s: missing `->' following value", full_s);
     }
     s += 2;
-    s = mf_parse_subfield(&load->dst, s);
-    if (*s != '\0') {
-        ovs_fatal(0, "%s: trailing garbage following destination", full_s);
+    error = mf_parse_subfield(&load->dst, s);
+    if (error) {
+        return error;
     }
 
     if (load->dst.n_bits < 64 && (value >> load->dst.n_bits) != 0) {
-        ovs_fatal(0, "%s: value %"PRIu64" does not fit into %d bits",
-                  full_s, value, load->dst.n_bits);
+        return xasprintf("%s: value %"PRIu64" does not fit into %d bits",
+                         full_s, value, load->dst.n_bits);
     }
 
     load->subvalue.be64[0] = htonll(0);
     load->subvalue.be64[1] = htonll(value);
+    return NULL;
 }
 \f
 /* nxm_format_reg_move(), nxm_format_reg_load(). */
@@ -1316,13 +1333,27 @@ nxm_reg_load(const struct mf_subfield *dst, uint64_t src_data,
 }
 \f
 /* nxm_parse_stack_action, works for both push() and pop(). */
-void
+
+/* Parses 's' as a "push" or "pop" action, in the form described in
+ * ovs-ofctl(8), into '*stack_action'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+char * WARN_UNUSED_RESULT
 nxm_parse_stack_action(struct ofpact_stack *stack_action, const char *s)
 {
-    s = mf_parse_subfield(&stack_action->subfield, s);
+    char *error;
+
+    error = mf_parse_subfield__(&stack_action->subfield, &s);
+    if (error) {
+        return error;
+    }
+
     if (*s != '\0') {
-        ovs_fatal(0, "%s: trailing garbage following push or pop", s);
+        return xasprintf("%s: trailing garbage following push or pop", s);
     }
+
+    return NULL;
 }
 
 void
index bb63b90..b03688b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 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.
@@ -20,6 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 #include <netinet/in.h>
+#include "compiler.h"
 #include "flow.h"
 #include "ofp-errors.h"
 #include "openvswitch/types.h"
@@ -57,8 +58,10 @@ char *oxm_match_to_string(const uint8_t *, unsigned int match_len);
 int nx_match_from_string(const char *, struct ofpbuf *);
 int oxm_match_from_string(const char *, struct ofpbuf *);
 
-void nxm_parse_reg_move(struct ofpact_reg_move *, const char *);
-void nxm_parse_reg_load(struct ofpact_reg_load *, const char *);
+char *nxm_parse_reg_move(struct ofpact_reg_move *, const char *)
+    WARN_UNUSED_RESULT;
+char *nxm_parse_reg_load(struct ofpact_reg_load *, const char *)
+    WARN_UNUSED_RESULT;
 
 void nxm_format_reg_move(const struct ofpact_reg_move *, struct ds *);
 void nxm_format_reg_load(const struct ofpact_reg_load *, struct ds *);
@@ -86,7 +89,8 @@ void nxm_execute_reg_load(const struct ofpact_reg_load *, struct flow *);
 void nxm_reg_load(const struct mf_subfield *, uint64_t src_data,
                   struct flow *);
 
-void nxm_parse_stack_action(struct ofpact_stack *, const char *);
+char *nxm_parse_stack_action(struct ofpact_stack *, const char *)
+    WARN_UNUSED_RESULT;
 
 void nxm_format_stack_push(const struct ofpact_stack *, struct ds *);
 void nxm_format_stack_pop(const struct ofpact_stack *, struct ds *);
index 2501e38..3ead49e 100644 (file)
@@ -519,34 +519,34 @@ enum ofptype {
                                       * OFPRAW_OFPST11_QUEUE_REPLY.
                                       * OFPRAW_OFPST13_QUEUE_REPLY. */
 
-    OFPTYPE_GROUP_REQUEST,           /* OFPRAW_OFPST11_GROUP_REQUEST. */
+    OFPTYPE_GROUP_STATS_REQUEST,     /* OFPRAW_OFPST11_GROUP_REQUEST. */
 
-    OFPTYPE_GROUP_REPLY,             /* OFPRAW_OFPST11_GROUP_REPLY.
+    OFPTYPE_GROUP_STATS_REPLY,       /* OFPRAW_OFPST11_GROUP_REPLY.
                                       * OFPRAW_OFPST13_GROUP_REPLY. */
 
-    OFPTYPE_GROUP_DESC_REQUEST,      /* OFPRAW_OFPST11_GROUP_DESC_REQUEST. */
+    OFPTYPE_GROUP_DESC_STATS_REQUEST, /* OFPRAW_OFPST11_GROUP_DESC_REQUEST. */
 
-    OFPTYPE_GROUP_DESC_REPLY,        /* OFPRAW_OFPST11_GROUP_DESC_REPLY. */
+    OFPTYPE_GROUP_DESC_STATS_REPLY,  /* OFPRAW_OFPST11_GROUP_DESC_REPLY. */
 
-    OFPTYPE_GROUP_FEATURES_REQUEST, /* OFPRAW_OFPST12_GROUP_FEATURES_REQUEST. */
+    OFPTYPE_GROUP_FEATURES_STATS_REQUEST, /* OFPRAW_OFPST12_GROUP_FEATURES_REQUEST. */
 
-    OFPTYPE_GROUP_FEATURES_REPLY,    /* OFPRAW_OFPST12_GROUP_FEATURES_REPLY. */
+    OFPTYPE_GROUP_FEATURES_STATS_REPLY, /* OFPRAW_OFPST12_GROUP_FEATURES_REPLY. */
 
-    OFPTYPE_METER_REQUEST,           /* OFPRAW_OFPST13_METER_REQUEST. */
+    OFPTYPE_METER_STATS_REQUEST,     /* OFPRAW_OFPST13_METER_REQUEST. */
 
-    OFPTYPE_METER_REPLY,             /* OFPRAW_OFPST13_METER_REPLY. */
+    OFPTYPE_METER_STATS_REPLY,       /* OFPRAW_OFPST13_METER_REPLY. */
 
-    OFPTYPE_METER_CONFIG_REQUEST,    /* OFPRAW_OFPST13_METER_CONFIG_REQUEST. */
+    OFPTYPE_METER_CONFIG_STATS_REQUEST, /* OFPRAW_OFPST13_METER_CONFIG_REQUEST. */
 
-    OFPTYPE_METER_CONFIG_REPLY,      /* OFPRAW_OFPST13_METER_CONFIG_REPLY. */
+    OFPTYPE_METER_CONFIG_STATS_REPLY, /* OFPRAW_OFPST13_METER_CONFIG_REPLY. */
 
-    OFPTYPE_METER_FEATURES_REQUEST, /* OFPRAW_OFPST13_METER_FEATURES_REQUEST. */
+    OFPTYPE_METER_FEATURES_STATS_REQUEST, /* OFPRAW_OFPST13_METER_FEATURES_REQUEST. */
 
-    OFPTYPE_METER_FEATURES_REPLY,    /* OFPRAW_OFPST13_METER_FEATURES_REPLY. */
+    OFPTYPE_METER_FEATURES_STATS_REPLY, /* OFPRAW_OFPST13_METER_FEATURES_REPLY. */
 
-    OFPTYPE_TABLE_FEATURES_REQUEST, /* OFPRAW_OFPST13_TABLE_FEATURES_REQUEST. */
+    OFPTYPE_TABLE_FEATURES_STATS_REQUEST, /* OFPRAW_OFPST13_TABLE_FEATURES_REQUEST. */
 
-    OFPTYPE_TABLE_FEATURES_REPLY,    /* OFPRAW_OFPST13_TABLE_FEATURES_REPLY. */
+    OFPTYPE_TABLE_FEATURES_STATS_REPLY, /* OFPRAW_OFPST13_TABLE_FEATURES_REPLY. */
 
     OFPTYPE_PORT_DESC_STATS_REQUEST, /* OFPRAW_OFPST_PORT_DESC_REQUEST. */
 
index b1e369c..618290b 100644 (file)
 
 VLOG_DEFINE_THIS_MODULE(ofp_parse);
 
-static void ofp_fatal(const char *flow, bool verbose, const char *format, ...)
-    NO_RETURN;
-
-static uint8_t
-str_to_u8(const char *str, const char *name)
+/* Parses 'str' as an 8-bit unsigned integer into '*valuep'.
+ *
+ * 'name' describes the value parsed in an error message, if any.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+str_to_u8(const char *str, const char *name, uint8_t *valuep)
 {
     int value;
 
-    if (!str_to_int(str, 10, &value) || value < 0 || value > 255) {
-        ovs_fatal(0, "invalid %s \"%s\"", name, str);
+    if (!str_to_int(str, 0, &value) || value < 0 || value > 255) {
+        return xasprintf("invalid %s \"%s\"", name, str);
     }
-    return value;
+    *valuep = value;
+    return NULL;
 }
 
-static uint16_t
-str_to_u16(const char *str, const char *name)
+/* Parses 'str' as a 16-bit unsigned integer into '*valuep'.
+ *
+ * 'name' describes the value parsed in an error message, if any.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+str_to_u16(const char *str, const char *name, uint16_t *valuep)
 {
     int value;
 
     if (!str_to_int(str, 0, &value) || value < 0 || value > 65535) {
-        ovs_fatal(0, "invalid %s \"%s\"", name, str);
+        return xasprintf("invalid %s \"%s\"", name, str);
     }
-    return value;
+    *valuep = value;
+    return NULL;
 }
 
-static uint32_t
-str_to_u32(const char *str)
+/* Parses 'str' as a 32-bit unsigned integer into '*valuep'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+str_to_u32(const char *str, uint32_t *valuep)
 {
     char *tail;
     uint32_t value;
 
     if (!str[0]) {
-        ovs_fatal(0, "missing required numeric argument");
+        return xstrdup("missing required numeric argument");
     }
 
     errno = 0;
     value = strtoul(str, &tail, 0);
     if (errno == EINVAL || errno == ERANGE || *tail) {
-        ovs_fatal(0, "invalid numeric format %s", str);
+        return xasprintf("invalid numeric format %s", str);
     }
-    return value;
+    *valuep = value;
+    return NULL;
 }
 
-static uint64_t
-str_to_u64(const char *str)
+/* Parses 'str' as an 64-bit unsigned integer into '*valuep'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+str_to_u64(const char *str, uint64_t *valuep)
 {
     char *tail;
     uint64_t value;
 
     if (!str[0]) {
-        ovs_fatal(0, "missing required numeric argument");
+        return xstrdup("missing required numeric argument");
     }
 
     errno = 0;
     value = strtoull(str, &tail, 0);
     if (errno == EINVAL || errno == ERANGE || *tail) {
-        ovs_fatal(0, "invalid numeric format %s", str);
+        return xasprintf("invalid numeric format %s", str);
     }
-    return value;
+    *valuep = value;
+    return NULL;
 }
 
-static void
+/* Parses 'str' as an 64-bit unsigned integer in network byte order into
+ * '*valuep'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+str_to_be64(const char *str, ovs_be64 *valuep)
+{
+    uint64_t value;
+    char *error;
+
+    error = str_to_u64(str, &value);
+    if (!error) {
+        *valuep = htonll(value);
+    }
+    return error;
+}
+
+/* Parses 'str' as an Ethernet address into 'mac'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 str_to_mac(const char *str, uint8_t mac[6])
 {
     if (sscanf(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))
         != ETH_ADDR_SCAN_COUNT) {
-        ovs_fatal(0, "invalid mac address %s", str);
+        return xasprintf("invalid mac address %s", str);
     }
+    return NULL;
 }
 
-static void
+/* Parses 'str' as an IP address into '*ip'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 str_to_ip(const char *str, ovs_be32 *ip)
 {
     struct in_addr in_addr;
 
     if (lookup_ip(str, &in_addr)) {
-        ovs_fatal(0, "%s: could not convert to IP address", str);
+        return xasprintf("%s: could not convert to IP address", str);
     }
     *ip = in_addr.s_addr;
+    return NULL;
 }
 
-static void
+/* Parses 'arg' as the argument to an "enqueue" action, and appends such an
+ * action to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 parse_enqueue(char *arg, struct ofpbuf *ofpacts)
 {
     char *sp = NULL;
@@ -131,33 +185,48 @@ parse_enqueue(char *arg, struct ofpbuf *ofpacts)
     struct ofpact_enqueue *enqueue;
 
     if (port == NULL || queue == NULL) {
-        ovs_fatal(0, "\"enqueue\" syntax is \"enqueue:PORT:QUEUE\"");
+        return xstrdup("\"enqueue\" syntax is \"enqueue:PORT:QUEUE\"");
     }
 
     enqueue = ofpact_put_ENQUEUE(ofpacts);
-    enqueue->port = u16_to_ofp(str_to_u32(port));
-    enqueue->queue = str_to_u32(queue);
+    if (!ofputil_port_from_string(port, &enqueue->port)) {
+        return xasprintf("%s: enqueue to unknown port", port);
+    }
+    return str_to_u32(queue, &enqueue->queue);
 }
 
-static void
-parse_output(char *arg, struct ofpbuf *ofpacts)
+/* Parses 'arg' as the argument to an "output" action, and appends such an
+ * action to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+parse_output(const char *arg, struct ofpbuf *ofpacts)
 {
     if (strchr(arg, '[')) {
         struct ofpact_output_reg *output_reg;
 
         output_reg = ofpact_put_OUTPUT_REG(ofpacts);
-        mf_parse_subfield(&output_reg->src, arg);
         output_reg->max_len = UINT16_MAX;
+        return mf_parse_subfield(&output_reg->src, arg);
     } else {
         struct ofpact_output *output;
 
         output = ofpact_put_OUTPUT(ofpacts);
-        output->port = u16_to_ofp(str_to_u32(arg));
         output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
+        if (!ofputil_port_from_string(arg, &output->port)) {
+            return xasprintf("%s: output to unknown port", arg);
+        }
+        return NULL;
     }
 }
 
-static void
+/* Parses 'arg' as the argument to an "resubmit" action, and appends such an
+ * action to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 parse_resubmit(char *arg, struct ofpbuf *ofpacts)
 {
     struct ofpact_resubmit *resubmit;
@@ -168,22 +237,39 @@ parse_resubmit(char *arg, struct ofpbuf *ofpacts)
     in_port_s = strsep(&arg, ",");
     if (in_port_s && in_port_s[0]) {
         if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) {
-            ovs_fatal(0, "%s: resubmit to unknown port", in_port_s);
+            return xasprintf("%s: resubmit to unknown port", in_port_s);
         }
     } else {
         resubmit->in_port = OFPP_IN_PORT;
     }
 
     table_s = strsep(&arg, ",");
-    resubmit->table_id = table_s && table_s[0] ? str_to_u32(table_s) : 255;
+    if (table_s && table_s[0]) {
+        uint32_t table_id;
+        char *error;
+
+        error = str_to_u32(table_s, &table_id);
+        if (error) {
+            return error;
+        }
+        resubmit->table_id = table_id;
+    } else {
+        resubmit->table_id = 255;
+    }
 
     if (resubmit->in_port == OFPP_IN_PORT && resubmit->table_id == 255) {
-        ovs_fatal(0, "at least one \"in_port\" or \"table\" must be specified "
-                  " on resubmit");
+        return xstrdup("at least one \"in_port\" or \"table\" must be "
+                       "specified  on resubmit");
     }
+    return NULL;
 }
 
-static void
+/* Parses 'arg' as the argument to a "note" action, and appends such an action
+ * to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 parse_note(const char *arg, struct ofpbuf *ofpacts)
 {
     struct ofpact_note *note;
@@ -202,7 +288,7 @@ parse_note(const char *arg, struct ofpbuf *ofpacts)
 
         byte = hexits_value(arg, 2, &ok);
         if (!ok) {
-            ovs_fatal(0, "bad hex digit in `note' argument");
+            return xstrdup("bad hex digit in `note' argument");
         }
         ofpbuf_put(ofpacts, &byte, 1);
 
@@ -212,26 +298,45 @@ parse_note(const char *arg, struct ofpbuf *ofpacts)
         arg += 2;
     }
     ofpact_update_len(ofpacts, &note->ofpact);
+    return NULL;
 }
 
-static void
+/* Parses 'arg' as the argument to a "fin_timeout" action, and appends such an
+ * action to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 parse_fin_timeout(struct ofpbuf *b, char *arg)
 {
     struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(b);
     char *key, *value;
 
     while (ofputil_parse_key_value(&arg, &key, &value)) {
+        char *error;
+
         if (!strcmp(key, "idle_timeout")) {
-            oft->fin_idle_timeout = str_to_u16(value, key);
+            error =  str_to_u16(value, key, &oft->fin_idle_timeout);
         } else if (!strcmp(key, "hard_timeout")) {
-            oft->fin_hard_timeout = str_to_u16(value, key);
+            error = str_to_u16(value, key, &oft->fin_hard_timeout);
         } else {
-            ovs_fatal(0, "invalid key '%s' in 'fin_timeout' argument", key);
+            error = xasprintf("invalid key '%s' in 'fin_timeout' argument",
+                              key);
+        }
+
+        if (error) {
+            return error;
         }
     }
+    return NULL;
 }
 
-static void
+/* Parses 'arg' as the argument to a "controller" action, and appends such an
+ * action to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 parse_controller(struct ofpbuf *b, char *arg)
 {
     enum ofp_packet_in_reason reason = OFPR_ACTION;
@@ -241,22 +346,31 @@ parse_controller(struct ofpbuf *b, char *arg)
     if (!arg[0]) {
         /* Use defaults. */
     } else if (strspn(arg, "0123456789") == strlen(arg)) {
-        max_len = str_to_u16(arg, "max_len");
+        char *error = str_to_u16(arg, "max_len", &max_len);
+        if (error) {
+            return error;
+        }
     } else {
         char *name, *value;
 
         while (ofputil_parse_key_value(&arg, &name, &value)) {
             if (!strcmp(name, "reason")) {
                 if (!ofputil_packet_in_reason_from_string(value, &reason)) {
-                    ovs_fatal(0, "unknown reason \"%s\"", value);
+                    return xasprintf("unknown reason \"%s\"", value);
                 }
             } else if (!strcmp(name, "max_len")) {
-                max_len = str_to_u16(value, "max_len");
+                char *error = str_to_u16(value, "max_len", &max_len);
+                if (error) {
+                    return error;
+                }
             } else if (!strcmp(name, "id")) {
-                controller_id = str_to_u16(value, "id");
+                char *error = str_to_u16(value, "id", &controller_id);
+                if (error) {
+                    return error;
+                }
             } else {
-                ovs_fatal(0, "unknown key \"%s\" parsing controller action",
-                          name);
+                return xasprintf("unknown key \"%s\" parsing controller "
+                                 "action", name);
             }
         }
     }
@@ -275,6 +389,8 @@ parse_controller(struct ofpbuf *b, char *arg)
         controller->reason = reason;
         controller->controller_id = controller_id;
     }
+
+    return NULL;
 }
 
 static void
@@ -290,7 +406,12 @@ parse_noargs_dec_ttl(struct ofpbuf *b)
     ofpact_update_len(b, &ids->ofpact);
 }
 
-static void
+/* Parses 'arg' as the argument to a "dec_ttl" action, and appends such an
+ * action to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 parse_dec_ttl(struct ofpbuf *b, char *arg)
 {
     if (*arg == '\0') {
@@ -310,68 +431,99 @@ parse_dec_ttl(struct ofpbuf *b, char *arg)
             ids->n_controllers++;
         }
         if (!ids->n_controllers) {
-            ovs_fatal(0, "dec_ttl_cnt_ids: expected at least one controller "
-                      "id.");
+            return xstrdup("dec_ttl_cnt_ids: expected at least one controller "
+                           "id.");
         }
         ofpact_update_len(b, &ids->ofpact);
     }
+    return NULL;
 }
 
-static void
+/* Parses 'arg' as the argument to a "set_mpls_ttl" action, and appends such an
+ * action to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 parse_set_mpls_ttl(struct ofpbuf *b, const char *arg)
 {
     struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(b);
 
     if (*arg == '\0') {
-        ovs_fatal(0, "parse_set_mpls_ttl: expected ttl.");
+        return xstrdup("parse_set_mpls_ttl: expected ttl.");
     }
 
     mpls_ttl->ttl = atoi(arg);
+    return NULL;
 }
 
-static void
-set_field_parse(const char *arg, struct ofpbuf *ofpacts)
+/* Parses a "set_field" action with argument 'arg', appending the parsed
+ * action to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+set_field_parse__(char *arg, struct ofpbuf *ofpacts)
 {
-    char *orig = xstrdup(arg);
     struct ofpact_reg_load *load = ofpact_put_REG_LOAD(ofpacts);
     char *value;
     char *delim;
     char *key;
     const struct mf_field *mf;
-    const char *error;
+    char *error;
     union mf_value mf_value;
 
-    value = orig;
-    delim = strstr(orig, "->");
+    value = arg;
+    delim = strstr(arg, "->");
     if (!delim) {
-        ovs_fatal(0, "%s: missing `->'", orig);
+        return xasprintf("%s: missing `->'", arg);
     }
     if (strlen(delim) <= strlen("->")) {
-        ovs_fatal(0, "%s: missing field name following `->'", orig);
+        return xasprintf("%s: missing field name following `->'", arg);
     }
 
     key = delim + strlen("->");
     mf = mf_from_name(key);
     if (!mf) {
-        ovs_fatal(0, "%s is not valid oxm field name", key);
+        return xasprintf("%s is not a valid OXM field name", key);
     }
     if (!mf->writable) {
-        ovs_fatal(0, "%s is not allowed to set", key);
+        return xasprintf("%s is read-only", key);
     }
 
     delim[0] = '\0';
     error = mf_parse_value(mf, value, &mf_value);
     if (error) {
-        ovs_fatal(0, "%s", error);
+        return error;
     }
     if (!mf_is_value_valid(mf, &mf_value)) {
-        ovs_fatal(0, "%s is not valid valid for field %s", value, key);
+        return xasprintf("%s is not a valid value for field %s", value, key);
     }
     ofpact_set_field_init(load, mf, &mf_value);
-    free(orig);
+
+    return NULL;
 }
 
-static void
+/* Parses 'arg' as the argument to a "set_field" action, and appends such an
+ * action to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+set_field_parse(const char *arg, struct ofpbuf *ofpacts)
+{
+    char *copy = xstrdup(arg);
+    char *error = set_field_parse__(copy, ofpacts);
+    free(copy);
+    return error;
+}
+
+/* Parses 'arg' as the argument to a "write_metadata" instruction, and appends
+ * such an action to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 parse_metadata(struct ofpbuf *b, char *arg)
 {
     struct ofpact_metadata *om;
@@ -380,51 +532,73 @@ parse_metadata(struct ofpbuf *b, char *arg)
     om = ofpact_put_WRITE_METADATA(b);
 
     if (mask) {
+        char *error;
+
         *mask = '\0';
-        om->mask = htonll(str_to_u64(mask + 1));
+        error = str_to_be64(mask + 1, &om->mask);
+        if (error) {
+            return error;
+        }
     } else {
         om->mask = htonll(UINT64_MAX);
     }
 
-    om->metadata = htonll(str_to_u64(arg));
+    return str_to_be64(arg, &om->metadata);
 }
 
-static void
+/* Parses 'arg' as the argument to a "sample" action, and appends such an
+ * action to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 parse_sample(struct ofpbuf *b, char *arg)
 {
     struct ofpact_sample *os = ofpact_put_SAMPLE(b);
     char *key, *value;
 
     while (ofputil_parse_key_value(&arg, &key, &value)) {
+        char *error = NULL;
+
         if (!strcmp(key, "probability")) {
-            os->probability = str_to_u16(value, "probability");
-            if (os->probability == 0) {
-                ovs_fatal(0, "invalid probability value \"%s\"", value);
+            error = str_to_u16(value, "probability", &os->probability);
+            if (!error && os->probability == 0) {
+                error = xasprintf("invalid probability value \"%s\"", value);
             }
         } else if (!strcmp(key, "collector_set_id")) {
-            os->collector_set_id = str_to_u32(value);
+            error = str_to_u32(value, &os->collector_set_id);
         } else if (!strcmp(key, "obs_domain_id")) {
-            os->obs_domain_id = str_to_u32(value);
+            error = str_to_u32(value, &os->obs_domain_id);
         } else if (!strcmp(key, "obs_point_id")) {
-            os->obs_point_id = str_to_u32(value);
+            error = str_to_u32(value, &os->obs_point_id);
         } else {
-            ovs_fatal(0, "invalid key \"%s\" in \"sample\" argument",
-                      key);
+            error = xasprintf("invalid key \"%s\" in \"sample\" argument",
+                              key);
+        }
+        if (error) {
+            return error;
         }
     }
     if (os->probability == 0) {
-        ovs_fatal(0, "non-zero \"probability\" must be specified on sample");
+        return xstrdup("non-zero \"probability\" must be specified on sample");
     }
+    return NULL;
 }
 
-static void
+/* Parses 'arg' as the argument to action 'code', and appends such an action to
+ * 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 parse_named_action(enum ofputil_action_code code,
                    char *arg, struct ofpbuf *ofpacts)
 {
+    size_t orig_size = ofpacts->size;
     struct ofpact_tunnel *tunnel;
-    uint16_t vid;
+    char *error = NULL;
     uint16_t ethertype;
-    ovs_be32 ip;
+    uint16_t vid;
     uint8_t pcp;
     uint8_t tos;
 
@@ -434,30 +608,37 @@ parse_named_action(enum ofputil_action_code code,
 
     case OFPUTIL_OFPAT10_OUTPUT:
     case OFPUTIL_OFPAT11_OUTPUT:
-        parse_output(arg, ofpacts);
+        error = parse_output(arg, ofpacts);
         break;
 
     case OFPUTIL_OFPAT10_SET_VLAN_VID:
     case OFPUTIL_OFPAT11_SET_VLAN_VID:
-        vid = str_to_u32(arg);
+        error = str_to_u16(arg, "VLAN VID", &vid);
+        if (error) {
+            return error;
+        }
+
         if (vid & ~VLAN_VID_MASK) {
-            ovs_fatal(0, "%s: not a valid VLAN VID", arg);
+            return xasprintf("%s: not a valid VLAN VID", arg);
         }
         ofpact_put_SET_VLAN_VID(ofpacts)->vlan_vid = vid;
         break;
 
     case OFPUTIL_OFPAT10_SET_VLAN_PCP:
     case OFPUTIL_OFPAT11_SET_VLAN_PCP:
-        pcp = str_to_u32(arg);
+        error = str_to_u8(arg, "VLAN PCP", &pcp);
+        if (error) {
+            return error;
+        }
+
         if (pcp & ~7) {
-            ovs_fatal(0, "%s: not a valid VLAN PCP", arg);
+            return xasprintf("%s: not a valid VLAN PCP", arg);
         }
         ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp;
         break;
 
     case OFPUTIL_OFPAT12_SET_FIELD:
-        set_field_parse(arg, ofpacts);
-        break;
+        return set_field_parse(arg, ofpacts);
 
     case OFPUTIL_OFPAT10_STRIP_VLAN:
     case OFPUTIL_OFPAT11_POP_VLAN:
@@ -465,46 +646,52 @@ parse_named_action(enum ofputil_action_code code,
         break;
 
     case OFPUTIL_OFPAT11_PUSH_VLAN:
-        ethertype = str_to_u16(arg, "ethertype");
+        error = str_to_u16(arg, "ethertype", &ethertype);
+        if (error) {
+            return error;
+        }
+
         if (ethertype != ETH_TYPE_VLAN_8021Q) {
             /* XXX ETH_TYPE_VLAN_8021AD case isn't supported */
-            ovs_fatal(0, "%s: not a valid VLAN ethertype", arg);
+            return xasprintf("%s: not a valid VLAN ethertype", arg);
         }
+
         ofpact_put_PUSH_VLAN(ofpacts);
         break;
 
     case OFPUTIL_OFPAT11_SET_QUEUE:
-        ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg);
+        error = str_to_u32(arg, &ofpact_put_SET_QUEUE(ofpacts)->queue_id);
         break;
 
-
     case OFPUTIL_OFPAT10_SET_DL_SRC:
     case OFPUTIL_OFPAT11_SET_DL_SRC:
-        str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac);
+        error = str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac);
         break;
 
     case OFPUTIL_OFPAT10_SET_DL_DST:
     case OFPUTIL_OFPAT11_SET_DL_DST:
-        str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac);
+        error = str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac);
         break;
 
     case OFPUTIL_OFPAT10_SET_NW_SRC:
     case OFPUTIL_OFPAT11_SET_NW_SRC:
-        str_to_ip(arg, &ip);
-        ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4 = ip;
+        error = str_to_ip(arg, &ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4);
         break;
 
     case OFPUTIL_OFPAT10_SET_NW_DST:
     case OFPUTIL_OFPAT11_SET_NW_DST:
-        str_to_ip(arg, &ip);
-        ofpact_put_SET_IPV4_DST(ofpacts)->ipv4 = ip;
+        error = str_to_ip(arg, &ofpact_put_SET_IPV4_DST(ofpacts)->ipv4);
         break;
 
     case OFPUTIL_OFPAT10_SET_NW_TOS:
     case OFPUTIL_OFPAT11_SET_NW_TOS:
-        tos = str_to_u32(arg);
+        error = str_to_u8(arg, "TOS", &tos);
+        if (error) {
+            return error;
+        }
+
         if (tos & ~IP_DSCP_MASK) {
-            ovs_fatal(0, "%s: not a valid TOS", arg);
+            return xasprintf("%s: not a valid TOS", arg);
         }
         ofpact_put_SET_IPV4_DSCP(ofpacts)->dscp = tos;
         break;
@@ -514,35 +701,37 @@ parse_named_action(enum ofputil_action_code code,
 
     case OFPUTIL_OFPAT10_SET_TP_SRC:
     case OFPUTIL_OFPAT11_SET_TP_SRC:
-        ofpact_put_SET_L4_SRC_PORT(ofpacts)->port = str_to_u32(arg);
+        error = str_to_u16(arg, "source port",
+                           &ofpact_put_SET_L4_SRC_PORT(ofpacts)->port);
         break;
 
     case OFPUTIL_OFPAT10_SET_TP_DST:
     case OFPUTIL_OFPAT11_SET_TP_DST:
-        ofpact_put_SET_L4_DST_PORT(ofpacts)->port = str_to_u32(arg);
+        error = str_to_u16(arg, "destination port",
+                           &ofpact_put_SET_L4_DST_PORT(ofpacts)->port);
         break;
 
     case OFPUTIL_OFPAT10_ENQUEUE:
-        parse_enqueue(arg, ofpacts);
+        error = parse_enqueue(arg, ofpacts);
         break;
 
     case OFPUTIL_NXAST_RESUBMIT:
-        parse_resubmit(arg, ofpacts);
+        error = parse_resubmit(arg, ofpacts);
         break;
 
     case OFPUTIL_NXAST_SET_TUNNEL:
     case OFPUTIL_NXAST_SET_TUNNEL64:
         tunnel = ofpact_put_SET_TUNNEL(ofpacts);
         tunnel->ofpact.compat = code;
-        tunnel->tun_id = str_to_u64(arg);
+        error = str_to_u64(arg, &tunnel->tun_id);
         break;
 
     case OFPUTIL_NXAST_WRITE_METADATA:
-        parse_metadata(ofpacts, arg);
+        error = parse_metadata(ofpacts, arg);
         break;
 
     case OFPUTIL_NXAST_SET_QUEUE:
-        ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg);
+        error = str_to_u32(arg, &ofpact_put_SET_QUEUE(ofpacts)->queue_id);
         break;
 
     case OFPUTIL_NXAST_POP_QUEUE:
@@ -550,27 +739,27 @@ parse_named_action(enum ofputil_action_code code,
         break;
 
     case OFPUTIL_NXAST_REG_MOVE:
-        nxm_parse_reg_move(ofpact_put_REG_MOVE(ofpacts), arg);
+        error = nxm_parse_reg_move(ofpact_put_REG_MOVE(ofpacts), arg);
         break;
 
     case OFPUTIL_NXAST_REG_LOAD:
-        nxm_parse_reg_load(ofpact_put_REG_LOAD(ofpacts), arg);
+        error = nxm_parse_reg_load(ofpact_put_REG_LOAD(ofpacts), arg);
         break;
 
     case OFPUTIL_NXAST_NOTE:
-        parse_note(arg, ofpacts);
+        error = parse_note(arg, ofpacts);
         break;
 
     case OFPUTIL_NXAST_MULTIPATH:
-        multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg);
+        error = multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg);
         break;
 
     case OFPUTIL_NXAST_BUNDLE:
-        bundle_parse(arg, ofpacts);
+        error = bundle_parse(arg, ofpacts);
         break;
 
     case OFPUTIL_NXAST_BUNDLE_LOAD:
-        bundle_parse_load(arg, ofpacts);
+        error = bundle_parse_load(arg, ofpacts);
         break;
 
     case OFPUTIL_NXAST_RESUBMIT_TABLE:
@@ -579,7 +768,7 @@ parse_named_action(enum ofputil_action_code code,
         NOT_REACHED();
 
     case OFPUTIL_NXAST_LEARN:
-        learn_parse(arg, ofpacts);
+        error = learn_parse(arg, ofpacts);
         break;
 
     case OFPUTIL_NXAST_EXIT:
@@ -587,12 +776,12 @@ parse_named_action(enum ofputil_action_code code,
         break;
 
     case OFPUTIL_NXAST_DEC_TTL:
-        parse_dec_ttl(ofpacts, arg);
+        error = parse_dec_ttl(ofpacts, arg);
         break;
 
     case OFPUTIL_NXAST_SET_MPLS_TTL:
     case OFPUTIL_OFPAT11_SET_MPLS_TTL:
-        parse_set_mpls_ttl(ofpacts, arg);
+        error = parse_set_mpls_ttl(ofpacts, arg);
         break;
 
     case OFPUTIL_OFPAT11_DEC_MPLS_TTL:
@@ -601,69 +790,90 @@ parse_named_action(enum ofputil_action_code code,
         break;
 
     case OFPUTIL_NXAST_FIN_TIMEOUT:
-        parse_fin_timeout(ofpacts, arg);
+        error = parse_fin_timeout(ofpacts, arg);
         break;
 
     case OFPUTIL_NXAST_CONTROLLER:
-        parse_controller(ofpacts, arg);
+        error = parse_controller(ofpacts, arg);
         break;
 
     case OFPUTIL_OFPAT11_PUSH_MPLS:
     case OFPUTIL_NXAST_PUSH_MPLS:
-        ofpact_put_PUSH_MPLS(ofpacts)->ethertype =
-            htons(str_to_u16(arg, "push_mpls"));
+        error = str_to_u16(arg, "push_mpls", &ethertype);
+        if (!error) {
+            ofpact_put_PUSH_MPLS(ofpacts)->ethertype = htons(ethertype);
+        }
         break;
 
     case OFPUTIL_OFPAT11_POP_MPLS:
     case OFPUTIL_NXAST_POP_MPLS:
-        ofpact_put_POP_MPLS(ofpacts)->ethertype =
-            htons(str_to_u16(arg, "pop_mpls"));
+        error = str_to_u16(arg, "pop_mpls", &ethertype);
+        if (!error) {
+            ofpact_put_POP_MPLS(ofpacts)->ethertype = htons(ethertype);
+        }
         break;
 
     case OFPUTIL_NXAST_STACK_PUSH:
-        nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg);
+        error = nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg);
         break;
     case OFPUTIL_NXAST_STACK_POP:
-        nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg);
+        error = nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg);
         break;
 
     case OFPUTIL_NXAST_SAMPLE:
-        parse_sample(ofpacts, arg);
+        error = parse_sample(ofpacts, arg);
         break;
     }
+
+    if (error) {
+        ofpacts->size = orig_size;
+    }
+    return error;
 }
 
-static bool
+/* Parses action 'act', with argument 'arg', and appends a parsed version to
+ * 'ofpacts'.
+ *
+ * 'n_actions' specifies the number of actions already parsed (for proper
+ * handling of "drop" actions).
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 str_to_ofpact__(char *pos, char *act, char *arg,
                 struct ofpbuf *ofpacts, int n_actions)
 {
     int code = ofputil_action_code_from_name(act);
     if (code >= 0) {
-        parse_named_action(code, arg, ofpacts);
+        return parse_named_action(code, arg, ofpacts);
     } else if (!strcasecmp(act, "drop")) {
         if (n_actions) {
-            ovs_fatal(0, "Drop actions must not be preceded by other "
-                      "actions");
+            return xstrdup("Drop actions must not be preceded by other "
+                           "actions");
         } else if (ofputil_parse_key_value(&pos, &act, &arg)) {
-            ovs_fatal(0, "Drop actions must not be followed by other "
-                      "actions");
+            return xstrdup("Drop actions must not be followed by other "
+                           "actions");
         }
-        return false;
     } else {
         ofp_port_t port;
         if (ofputil_port_from_string(act, &port)) {
             ofpact_put_OUTPUT(ofpacts)->port = port;
         } else {
-            ovs_fatal(0, "Unknown action: %s", act);
+            return xasprintf("Unknown action: %s", act);
         }
     }
 
-    return true;
+    return NULL;
 }
 
-static void
+/* Parses 'str' as a series of actions, and appends them to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 str_to_ofpacts(char *str, struct ofpbuf *ofpacts)
 {
+    size_t orig_size = ofpacts->size;
     char *pos, *act, *arg;
     enum ofperr error;
     int n_actions;
@@ -671,24 +881,34 @@ str_to_ofpacts(char *str, struct ofpbuf *ofpacts)
     pos = str;
     n_actions = 0;
     while (ofputil_parse_key_value(&pos, &act, &arg)) {
-        if (!str_to_ofpact__(pos, act, arg, ofpacts, n_actions)) {
-            break;
+        char *error = str_to_ofpact__(pos, act, arg, ofpacts, n_actions);
+        if (error) {
+            ofpacts->size = orig_size;
+            return error;
         }
         n_actions++;
     }
 
     error = ofpacts_verify(ofpacts->data, ofpacts->size);
     if (error) {
-        ovs_fatal(0, "Incorrect action ordering");
+        ofpacts->size = orig_size;
+        return xstrdup("Incorrect action ordering");
     }
 
     ofpact_pad(ofpacts);
+    return NULL;
 }
 
-static void
+/* Parses 'arg' as the argument to instruction 'type', and appends such an
+ * instruction to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 parse_named_instruction(enum ovs_instruction_type type,
                         char *arg, struct ofpbuf *ofpacts)
 {
+    char *error_s = NULL;
     enum ofperr error;
 
     switch (type) {
@@ -698,7 +918,7 @@ parse_named_instruction(enum ovs_instruction_type type,
 
     case OVSINST_OFPIT11_WRITE_ACTIONS:
         /* XXX */
-        ovs_fatal(0, "instruction write-actions is not supported yet");
+        error_s = xstrdup("instruction write-actions is not supported yet");
         break;
 
     case OVSINST_OFPIT11_CLEAR_ACTIONS:
@@ -706,35 +926,45 @@ parse_named_instruction(enum ovs_instruction_type type,
         break;
 
     case OVSINST_OFPIT13_METER:
-        ofpact_put_METER(ofpacts)->meter_id = str_to_u32(arg);
+        error_s = str_to_u32(arg, &ofpact_put_METER(ofpacts)->meter_id);
         break;
 
     case OVSINST_OFPIT11_WRITE_METADATA:
-        parse_metadata(ofpacts, arg);
+        error_s = parse_metadata(ofpacts, arg);
         break;
 
     case OVSINST_OFPIT11_GOTO_TABLE: {
         struct ofpact_goto_table *ogt = ofpact_put_GOTO_TABLE(ofpacts);
         char *table_s = strsep(&arg, ",");
         if (!table_s || !table_s[0]) {
-            ovs_fatal(0, "instruction goto-table needs table id");
+            return xstrdup("instruction goto-table needs table id");
         }
-        ogt->table_id = str_to_u8(table_s, "table");
+        error_s = str_to_u8(table_s, "table", &ogt->table_id);
         break;
     }
     }
 
+    if (error_s) {
+        return error_s;
+    }
+
     /* If write_metadata is specified as an action AND an instruction, ofpacts
        could be invalid. */
     error = ofpacts_verify(ofpacts->data, ofpacts->size);
     if (error) {
-        ovs_fatal(0, "Incorrect instruction ordering");
+        return xstrdup("Incorrect instruction ordering");
     }
+    return NULL;
 }
 
-static void
+/* Parses 'str' as a series of instructions, and appends them to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 str_to_inst_ofpacts(char *str, struct ofpbuf *ofpacts)
 {
+    size_t orig_size = ofpacts->size;
     char *pos, *inst, *arg;
     int type;
     const char *prev_inst = NULL;
@@ -745,8 +975,10 @@ str_to_inst_ofpacts(char *str, struct ofpbuf *ofpacts)
     while (ofputil_parse_key_value(&pos, &inst, &arg)) {
         type = ovs_instruction_type_from_name(inst);
         if (type < 0) {
-            if (!str_to_ofpact__(pos, inst, arg, ofpacts, n_actions)) {
-                break;
+            char *error = str_to_ofpact__(pos, inst, arg, ofpacts, n_actions);
+            if (error) {
+                ofpacts->size = orig_size;
+                return error;
             }
 
             type = OVSINST_OFPIT11_APPLY_ACTIONS;
@@ -755,19 +987,26 @@ str_to_inst_ofpacts(char *str, struct ofpbuf *ofpacts)
                 continue;
             }
         } else if (type == OVSINST_OFPIT11_APPLY_ACTIONS) {
-            ovs_fatal(0, "%s isn't supported. Just write actions then "
-                      "it is interpreted as apply_actions", inst);
+            ofpacts->size = orig_size;
+            return xasprintf("%s isn't supported. Just write actions then "
+                             "it is interpreted as apply_actions", inst);
         } else {
-            parse_named_instruction(type, arg, ofpacts);
+            char *error = parse_named_instruction(type, arg, ofpacts);
+            if (error) {
+                ofpacts->size = orig_size;
+                return error;
+            }
         }
 
-        if (type == prev_type) {
-            ovs_fatal(0, "instruction can be specified at most once: %s",
-                      inst);
-        }
         if (type <= prev_type) {
-            ovs_fatal(0, "Instruction %s must be specified before %s",
-                      inst, prev_inst);
+            ofpacts->size = orig_size;
+            if (type == prev_type) {
+                return xasprintf("instruction %s may be specified only once",
+                                 inst);
+            } else {
+                return xasprintf("instruction %s must be specified before %s",
+                                 inst, prev_inst);
+            }
         }
 
         prev_inst = inst;
@@ -775,6 +1014,8 @@ str_to_inst_ofpacts(char *str, struct ofpbuf *ofpacts)
         n_actions++;
     }
     ofpact_pad(ofpacts);
+
+    return NULL;
 }
 
 struct protocol {
@@ -813,44 +1054,26 @@ parse_protocol(const char *name, const struct protocol **p_out)
     return false;
 }
 
-static void
-ofp_fatal(const char *flow, bool verbose, const char *format, ...)
-{
-    va_list args;
-
-    if (verbose) {
-        fprintf(stderr, "%s:\n", flow);
-    }
-
-    va_start(args, format);
-    ovs_fatal_valist(0, format, args);
-}
-
-static void
+/* Parses 's' as the (possibly masked) value of field 'mf', and updates
+ * 'match' appropriately.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
 parse_field(const struct mf_field *mf, const char *s, struct match *match)
 {
     union mf_value value, mask;
     char *error;
 
     error = mf_parse(mf, s, &value, &mask);
-    if (error) {
-        ovs_fatal(0, "%s", error);
+    if (!error) {
+        mf_set(mf, &value, &mask, match);
     }
-
-    mf_set(mf, &value, &mask, match);
+    return error;
 }
 
-/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
- * page) into 'fm' for sending the specified flow_mod 'command' to a switch.
- * If 'actions' is specified, an action must be in 'string' and may be expanded
- * or reallocated.
- *
- * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_*
- * constant for 'command'.  To parse syntax for an OFPST_FLOW or
- * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. */
-void
-parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
-              bool verbose)
+static char * WARN_UNUSED_RESULT
+parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string)
 {
     enum {
         F_OUT_PORT = 1 << 0,
@@ -859,7 +1082,6 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
         F_PRIORITY = 1 << 4,
         F_FLAGS = 1 << 5,
     } fields;
-    char *string = xstrdup(str_);
     char *save_ptr = NULL;
     char *act_str = NULL;
     char *name;
@@ -903,6 +1125,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     } else{
         fm->new_cookie = htonll(0);
     }
+    fm->modify_cookie = false;
     fm->table_id = 0xff;
     fm->command = command;
     fm->idle_timeout = OFP_FLOW_PERMANENT;
@@ -913,13 +1136,13 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     if (fields & F_ACTIONS) {
         act_str = strstr(string, "action");
         if (!act_str) {
-            ofp_fatal(str_, verbose, "must specify an action");
+            return xstrdup("must specify an action");
         }
         *act_str = '\0';
 
         act_str = strchr(act_str + 1, '=');
         if (!act_str) {
-            ofp_fatal(str_, verbose, "must specify an action");
+            return xstrdup("must specify an action");
         }
 
         act_str++;
@@ -927,6 +1150,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
          name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
         const struct protocol *p;
+        char *error = NULL;
 
         if (parse_protocol(name, &p)) {
             match_set_dl_type(&fm->match, htons(p->dl_type));
@@ -948,44 +1172,51 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
 
             value = strtok_r(NULL, ", \t\r\n", &save_ptr);
             if (!value) {
-                ofp_fatal(str_, verbose, "field %s missing value", name);
+                return xasprintf("field %s missing value", name);
             }
 
             if (!strcmp(name, "table")) {
-                fm->table_id = str_to_u8(value, name);
+                error = str_to_u8(value, "table", &fm->table_id);
             } else if (!strcmp(name, "out_port")) {
                 if (!ofputil_port_from_string(value, &fm->out_port)) {
-                    ofp_fatal(str_, verbose, "%s is not a valid OpenFlow port",
-                              name);
+                    error = xasprintf("%s is not a valid OpenFlow port",
+                                      value);
                 }
             } else if (fields & F_PRIORITY && !strcmp(name, "priority")) {
-                fm->priority = str_to_u16(value, name);
+                uint16_t priority;
+
+                error = str_to_u16(value, name, &priority);
+                fm->priority = priority;
             } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) {
-                fm->idle_timeout = str_to_u16(value, name);
+                error = str_to_u16(value, name, &fm->idle_timeout);
             } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) {
-                fm->hard_timeout = str_to_u16(value, name);
+                error = str_to_u16(value, name, &fm->hard_timeout);
             } else if (!strcmp(name, "cookie")) {
                 char *mask = strchr(value, '/');
 
                 if (mask) {
                     /* A mask means we're searching for a cookie. */
                     if (command == OFPFC_ADD) {
-                        ofp_fatal(str_, verbose, "flow additions cannot use "
-                                  "a cookie mask");
+                        return xstrdup("flow additions cannot use "
+                                       "a cookie mask");
                     }
                     *mask = '\0';
-                    fm->cookie = htonll(str_to_u64(value));
-                    fm->cookie_mask = htonll(str_to_u64(mask+1));
+                    error = str_to_be64(value, &fm->cookie);
+                    if (error) {
+                        return error;
+                    }
+                    error = str_to_be64(mask + 1, &fm->cookie_mask);
                 } else {
                     /* No mask means that the cookie is being set. */
                     if (command != OFPFC_ADD && command != OFPFC_MODIFY
-                            && command != OFPFC_MODIFY_STRICT) {
-                        ofp_fatal(str_, verbose, "cannot set cookie");
+                        && command != OFPFC_MODIFY_STRICT) {
+                        return xstrdup("cannot set cookie");
                     }
-                    fm->new_cookie = htonll(str_to_u64(value));
+                    error = str_to_be64(value, &fm->new_cookie);
+                    fm->modify_cookie = true;
                 }
             } else if (mf_from_name(name)) {
-                parse_field(mf_from_name(name), value, &fm->match);
+                error = parse_field(mf_from_name(name), value, &fm->match);
             } else if (!strcmp(name, "duration")
                        || !strcmp(name, "n_packets")
                        || !strcmp(name, "n_bytes")
@@ -995,12 +1226,16 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
                  * "ovs-ofctl dump-flows" back into commands that parse
                  * flows. */
             } else {
-                ofp_fatal(str_, verbose, "unknown keyword %s", name);
+                error = xasprintf("unknown keyword %s", name);
+            }
+
+            if (error) {
+                return error;
             }
         }
     }
     if (!fm->cookie_mask && fm->new_cookie == htonll(UINT64_MAX)
-            && (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) {
+        && (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
          * default of zero. */
@@ -1008,40 +1243,69 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     }
     if (fields & F_ACTIONS) {
         struct ofpbuf ofpacts;
-        enum ofperr err;
+        char *error;
 
         ofpbuf_init(&ofpacts, 32);
-        str_to_inst_ofpacts(act_str, &ofpacts);
-        fm->ofpacts_len = ofpacts.size;
-        fm->ofpacts = ofpbuf_steal_data(&ofpacts);
-
-        err = ofpacts_check(fm->ofpacts, fm->ofpacts_len, &fm->match.flow,
-                            OFPP_MAX, 0);
-        if (err) {
-            exit(EXIT_FAILURE);
+        error = str_to_inst_ofpacts(act_str, &ofpacts);
+        if (!error) {
+            enum ofperr err;
+
+            err = ofpacts_check(ofpacts.data, ofpacts.size, &fm->match.flow,
+                                OFPP_MAX, 0);
+            if (err) {
+                error = xasprintf("actions are invalid with specified match "
+                                  "(%s)", ofperr_to_string(err));
+            }
+        }
+        if (error) {
+            ofpbuf_uninit(&ofpacts);
+            return error;
         }
 
+        fm->ofpacts_len = ofpacts.size;
+        fm->ofpacts = ofpbuf_steal_data(&ofpacts);
     } else {
         fm->ofpacts_len = 0;
         fm->ofpacts = NULL;
     }
 
-    free(string);
+    return NULL;
 }
 
 /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
- * page) into 'mm' for sending the specified meter_mod 'command' to a switch.
- */
-void
-parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
-                        int command, bool verbose)
+ * page) into 'fm' for sending the specified flow_mod 'command' to a switch.
+ *
+ * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_*
+ * constant for 'command'.  To parse syntax for an OFPST_FLOW or
+ * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+char * WARN_UNUSED_RESULT
+parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_)
+{
+    char *string = xstrdup(str_);
+    char *error;
+
+    error = parse_ofp_str__(fm, command, string);
+    if (error) {
+        fm->ofpacts = NULL;
+        fm->ofpacts_len = 0;
+    }
+
+    free(string);
+    return error;
+}
+
+static char * WARN_UNUSED_RESULT
+parse_ofp_meter_mod_str__(struct ofputil_meter_mod *mm, char *string,
+                          struct ofpbuf *bands, int command)
 {
     enum {
         F_METER = 1 << 0,
         F_FLAGS = 1 << 1,
         F_BANDS = 1 << 2,
     } fields;
-    char *string = xstrdup(str_);
     char *save_ptr = NULL;
     char *band_str = NULL;
     char *name;
@@ -1073,13 +1337,13 @@ parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
     if (fields & F_BANDS) {
         band_str = strstr(string, "band");
         if (!band_str) {
-            ofp_fatal(str_, verbose, "must specify bands");
+            return xstrdup("must specify bands");
         }
         *band_str = '\0';
 
         band_str = strchr(band_str + 1, '=');
         if (!band_str) {
-            ofp_fatal(str_, verbose, "must specify bands");
+            return xstrdup("must specify bands");
         }
 
         band_str++;
@@ -1100,7 +1364,7 @@ parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
 
             value = strtok_r(NULL, ", \t\r\n", &save_ptr);
             if (!value) {
-                ofp_fatal(str_, verbose, "field %s missing value", name);
+                return xasprintf("field %s missing value", name);
             }
 
             if (!strcmp(name, "meter")) {
@@ -1111,32 +1375,31 @@ parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
                 } else if (!strcmp(value, "slowpath")) {
                     mm->meter.meter_id = OFPM13_SLOWPATH;
                 } else {
-                    mm->meter.meter_id = str_to_u32(value);
+                    char *error = str_to_u32(value, &mm->meter.meter_id);
+                    if (error) {
+                        return error;
+                    }
                     if (mm->meter.meter_id > OFPM13_MAX) {
-                        ofp_fatal(str_, verbose, "invalid value for %s", name);
+                        return xasprintf("invalid value for %s", name);
                     }
                 }
             } else {
-                ofp_fatal(str_, verbose, "unknown keyword %s", name);
+                return xasprintf("unknown keyword %s", name);
             }
         }
     }
     if (fields & F_METER && !mm->meter.meter_id) {
-        ofp_fatal(str_, verbose, "must specify 'meter'");
+        return xstrdup("must specify 'meter'");
     }
     if (fields & F_FLAGS && !mm->meter.flags) {
-        ofp_fatal(str_, verbose,
-                  "meter must specify either 'kbps' or 'pktps'");
+        return xstrdup("meter must specify either 'kbps' or 'pktps'");
     }
 
     if (fields & F_BANDS) {
-        struct ofpbuf bands;
         uint16_t n_bands = 0;
         struct ofputil_meter_band *band = NULL;
         int i;
 
-        ofpbuf_init(&bands, 64);
-
         for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name;
              name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
 
@@ -1144,12 +1407,12 @@ parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
 
             value = strtok_r(NULL, ", \t\r\n", &save_ptr);
             if (!value) {
-                ofp_fatal(str_, verbose, "field %s missing value", name);
+                return xasprintf("field %s missing value", name);
             }
 
             if (!strcmp(name, "type")) {
                 /* Start a new band */
-                band = ofpbuf_put_zeros(&bands, sizeof *band);
+                band = ofpbuf_put_zeros(bands, sizeof *band);
                 n_bands++;
 
                 if (!strcmp(value, "drop")) {
@@ -1157,59 +1420,66 @@ parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
                 } else if (!strcmp(value, "dscp_remark")) {
                     band->type = OFPMBT13_DSCP_REMARK;
                 } else {
-                    ofp_fatal(str_, verbose, "field %s unknown value %s", name,
-                              value);
+                    return xasprintf("field %s unknown value %s", name, value);
                 }
             } else if (!band || !band->type) {
-                ofp_fatal(str_, verbose,
-                          "band must start with the 'type' keyword");
+                return xstrdup("band must start with the 'type' keyword");
             } else if (!strcmp(name, "rate")) {
-                band->rate = str_to_u32(value);
+                char *error = str_to_u32(value, &band->rate);
+                if (error) {
+                    return error;
+                }
             } else if (!strcmp(name, "burst_size")) {
-                band->burst_size = str_to_u32(value);
+                char *error = str_to_u32(value, &band->burst_size);
+                if (error) {
+                    return error;
+                }
             } else if (!strcmp(name, "prec_level")) {
-                band->prec_level = str_to_u8(value, name);
+                char *error = str_to_u8(value, name, &band->prec_level);
+                if (error) {
+                    return error;
+                }
             } else {
-                ofp_fatal(str_, verbose, "unknown keyword %s", name);
+                return xasprintf("unknown keyword %s", name);
             }
         }
         /* validate bands */
         if (!n_bands) {
-            ofp_fatal(str_, verbose, "meter must have bands");
+            return xstrdup("meter must have bands");
         }
 
         mm->meter.n_bands = n_bands;
-        mm->meter.bands = ofpbuf_steal_data(&bands);
+        mm->meter.bands = ofpbuf_steal_data(bands);
 
         for (i = 0; i < n_bands; ++i) {
             band = &mm->meter.bands[i];
 
             if (!band->type) {
-                ofp_fatal(str_, verbose, "band must have 'type'");
+                return xstrdup("band must have 'type'");
             }
             if (band->type == OFPMBT13_DSCP_REMARK) {
                 if (!band->prec_level) {
-                    ofp_fatal(str_, verbose, "'dscp_remark' band must have"
-                              " 'prec_level'");
+                    return xstrdup("'dscp_remark' band must have"
+                                   " 'prec_level'");
                 }
             } else {
                 if (band->prec_level) {
-                    ofp_fatal(str_, verbose, "Only 'dscp_remark' band may have"
-                              " 'prec_level'");
+                    return xstrdup("Only 'dscp_remark' band may have"
+                                   " 'prec_level'");
                 }
             }
             if (!band->rate) {
-                ofp_fatal(str_, verbose, "band must have 'rate'");
+                return xstrdup("band must have 'rate'");
             }
             if (mm->meter.flags & OFPMF13_BURST) {
                 if (!band->burst_size) {
-                    ofp_fatal(str_, verbose, "band must have 'burst_size' "
-                              "when 'burst' flag is set");
+                    return xstrdup("band must have 'burst_size' "
+                                   "when 'burst' flag is set");
                 }
             } else {
                 if (band->burst_size) {
-                    ofp_fatal(str_, verbose, "band may have 'burst_size' only "
-                              "when 'burst' flag is set");
+                    return xstrdup("band may have 'burst_size' only "
+                                   "when 'burst' flag is set");
                 }
             }
         }
@@ -1218,18 +1488,39 @@ parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
         mm->meter.bands = NULL;
     }
 
+    return NULL;
+}
+
+/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
+ * page) into 'mm' for sending the specified meter_mod 'command' to a switch.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+char * WARN_UNUSED_RESULT
+parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
+                        int command)
+{
+    struct ofpbuf bands;
+    char *string;
+    char *error;
+
+    ofpbuf_init(&bands, 64);
+    string = xstrdup(str_);
+
+    error = parse_ofp_meter_mod_str__(mm, string, &bands, command);
+
     free(string);
+    ofpbuf_uninit(&bands);
+
+    return error;
 }
 
-/* Convert 'str_' (as described in the documentation for the "monitor" command
- * in the ovs-ofctl man page) into 'fmr'. */
-void
-parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
-                           const char *str_)
+static char * WARN_UNUSED_RESULT
+parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr,
+                             const char *str_, char *string)
 {
     static uint32_t id;
 
-    char *string = xstrdup(str_);
     char *save_ptr = NULL;
     char *name;
 
@@ -1266,95 +1557,162 @@ parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
 
             value = strtok_r(NULL, ", \t\r\n", &save_ptr);
             if (!value) {
-                ovs_fatal(0, "%s: field %s missing value", str_, name);
+                return xasprintf("%s: field %s missing value", str_, name);
             }
 
             if (!strcmp(name, "table")) {
-                fmr->table_id = str_to_u8(value, name);
+                char *error = str_to_u8(value, "table", &fmr->table_id);
+                if (error) {
+                    return error;
+                }
             } else if (!strcmp(name, "out_port")) {
                 fmr->out_port = u16_to_ofp(atoi(value));
             } else if (mf_from_name(name)) {
-                parse_field(mf_from_name(name), value, &fmr->match);
+                char *error;
+
+                error = parse_field(mf_from_name(name), value, &fmr->match);
+                if (error) {
+                    return error;
+                }
             } else {
-                ovs_fatal(0, "%s: unknown keyword %s", str_, name);
+                return xasprintf("%s: unknown keyword %s", str_, name);
             }
         }
     }
+    return NULL;
+}
+
+/* Convert 'str_' (as described in the documentation for the "monitor" command
+ * in the ovs-ofctl man page) into 'fmr'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+char * WARN_UNUSED_RESULT
+parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
+                           const char *str_)
+{
+    char *string = xstrdup(str_);
+    char *error = parse_flow_monitor_request__(fmr, str_, string);
     free(string);
+    return error;
 }
 
 /* Parses 's' as a set of OpenFlow actions and appends the actions to
  * 'actions'.
  *
- * Prints an error on stderr and aborts the program if 's' syntax is
- * invalid. */
-void
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+char * WARN_UNUSED_RESULT
 parse_ofpacts(const char *s_, struct ofpbuf *ofpacts)
 {
     char *s = xstrdup(s_);
-    str_to_ofpacts(s, ofpacts);
+    char *error = str_to_ofpacts(s, ofpacts);
     free(s);
+
+    return error;
 }
 
 /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command'
- * (one of OFPFC_*) into 'fm'. */
-void
+ * (one of OFPFC_*) into 'fm'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+char * WARN_UNUSED_RESULT
 parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
-                       uint16_t command, bool verbose)
+                       uint16_t command)
 {
-    struct match match_copy;
-
-    parse_ofp_str(fm, command, string, verbose);
+    char *error = parse_ofp_str(fm, command, string);
+    if (!error) {
+        /* Normalize a copy of the match.  This ensures that non-normalized
+         * flows get logged but doesn't affect what gets sent to the switch, so
+         * that the switch can do whatever it likes with the flow. */
+        struct match match_copy = fm->match;
+        ofputil_normalize_match(&match_copy);
+    }
 
-    /* Normalize a copy of the match.  This ensures that non-normalized flows
-     * get logged but doesn't affect what gets sent to the switch, so that the
-     * switch can do whatever it likes with the flow. */
-    match_copy = fm->match;
-    ofputil_normalize_match(&match_copy);
+    return error;
 }
 
-void
+/* Opens file 'file_name' and reads each line as a flow_mod of the specified
+ * type (one of OFPFC_*).  Stores each flow_mod in '*fm', an array allocated
+ * on the caller's behalf, and the number of flow_mods in '*n_fms'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+char * WARN_UNUSED_RESULT
 parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
                         struct ofputil_flow_mod **fms, size_t *n_fms)
 {
     size_t allocated_fms;
+    int line_number;
     FILE *stream;
     struct ds s;
 
+    *fms = NULL;
+    *n_fms = 0;
+
     stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r");
     if (stream == NULL) {
-        ovs_fatal(errno, "%s: open", file_name);
+        return xasprintf("%s: open failed (%s)",
+                         file_name, ovs_strerror(errno));
     }
 
     allocated_fms = *n_fms;
     ds_init(&s);
-    while (!ds_get_preprocessed_line(&s, stream)) {
+    line_number = 0;
+    while (!ds_get_preprocessed_line(&s, stream, &line_number)) {
+        char *error;
+
         if (*n_fms >= allocated_fms) {
             *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms);
         }
-        parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command, false);
+        error = parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command);
+        if (error) {
+            size_t i;
+
+            for (i = 0; i < *n_fms; i++) {
+                free((*fms)[i].ofpacts);
+            }
+            free(*fms);
+            *fms = NULL;
+            *n_fms = 0;
+
+            ds_destroy(&s);
+            if (stream != stdin) {
+                fclose(stream);
+            }
+
+            return xasprintf("%s:%d: %s", file_name, line_number, error);
+        }
         *n_fms += 1;
     }
-    ds_destroy(&s);
 
+    ds_destroy(&s);
     if (stream != stdin) {
         fclose(stream);
     }
+    return NULL;
 }
 
-void
+char * WARN_UNUSED_RESULT
 parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
                                  bool aggregate, const char *string)
 {
     struct ofputil_flow_mod fm;
+    char *error;
+
+    error = parse_ofp_str(&fm, -1, string);
+    if (error) {
+        return error;
+    }
 
-    parse_ofp_str(&fm, -1, string, false);
     fsr->aggregate = aggregate;
     fsr->cookie = fm.cookie;
     fsr->cookie_mask = fm.cookie_mask;
     fsr->match = fm.match;
     fsr->out_port = fm.out_port;
     fsr->table_id = fm.table_id;
+    return NULL;
 }
 
 /* Parses a specification of a flow from 's' into 'flow'.  's' must take the
index 6ee25a4..707f5d8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 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.
@@ -22,6 +22,7 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
+#include "compiler.h"
 
 struct flow;
 struct ofpbuf;
@@ -30,26 +31,31 @@ struct ofputil_flow_monitor_request;
 struct ofputil_flow_stats_request;
 struct ofputil_meter_mod;
 
-void parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
-                   bool verbose);
+char *parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_)
+    WARN_UNUSED_RESULT;
 
-void parse_ofp_flow_mod_str(struct ofputil_flow_mod *, const char *string,
-                            uint16_t command, bool verbose);
-void parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
-                             struct ofputil_flow_mod **fms, size_t *n_fms);
+char *parse_ofp_flow_mod_str(struct ofputil_flow_mod *, const char *string,
+                            uint16_t command)
+    WARN_UNUSED_RESULT;
+char *parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
+                              struct ofputil_flow_mod **fms, size_t *n_fms)
+    WARN_UNUSED_RESULT;
 
-void parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *,
-                                      bool aggregate, const char *string);
+char *parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *,
+                                       bool aggregate, const char *string)
+    WARN_UNUSED_RESULT;
 
-
-void parse_ofpacts(const char *, struct ofpbuf *ofpacts);
+char *parse_ofpacts(const char *, struct ofpbuf *ofpacts)
+    WARN_UNUSED_RESULT;
 
 char *parse_ofp_exact_flow(struct flow *, const char *);
 
-void parse_ofp_meter_mod_str(struct ofputil_meter_mod *, const char *string,
-                             int command, bool verbose);
+char *parse_ofp_meter_mod_str(struct ofputil_meter_mod *, const char *string,
+                             int command)
+    WARN_UNUSED_RESULT;
 
-void parse_flow_monitor_request(struct ofputil_flow_monitor_request *,
-                                const char *);
+char *parse_flow_monitor_request(struct ofputil_flow_monitor_request *,
+                                const char *)
+    WARN_UNUSED_RESULT;
 
 #endif /* ofp-parse.h */
index 76bd09c..1d8b98c 100644 (file)
@@ -2142,14 +2142,14 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
     case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
     case OFPTYPE_GET_ASYNC_REQUEST:
     case OFPTYPE_GET_ASYNC_REPLY:
-    case OFPTYPE_GROUP_REQUEST:
-    case OFPTYPE_GROUP_REPLY:
-    case OFPTYPE_GROUP_DESC_REQUEST:
-    case OFPTYPE_GROUP_DESC_REPLY:
-    case OFPTYPE_GROUP_FEATURES_REQUEST:
-    case OFPTYPE_GROUP_FEATURES_REPLY:
-    case OFPTYPE_TABLE_FEATURES_REQUEST:
-    case OFPTYPE_TABLE_FEATURES_REPLY:
+    case OFPTYPE_GROUP_STATS_REQUEST:
+    case OFPTYPE_GROUP_STATS_REPLY:
+    case OFPTYPE_GROUP_DESC_STATS_REQUEST:
+    case OFPTYPE_GROUP_DESC_STATS_REPLY:
+    case OFPTYPE_GROUP_FEATURES_STATS_REQUEST:
+    case OFPTYPE_GROUP_FEATURES_STATS_REPLY:
+    case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
+    case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
         ofp_print_not_implemented(string);
         break;
 
@@ -2218,30 +2218,30 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         ofp_print_role_message(string, oh);
         break;
 
-    case OFPTYPE_METER_REQUEST:
-    case OFPTYPE_METER_CONFIG_REQUEST:
+    case OFPTYPE_METER_STATS_REQUEST:
+    case OFPTYPE_METER_CONFIG_STATS_REQUEST:
         ofp_print_stats_request(string, oh);
         ofp_print_meter_stats_request(string, oh);
         break;
 
-    case OFPTYPE_METER_REPLY:
+    case OFPTYPE_METER_STATS_REPLY:
         ofp_print_stats_reply(string, oh);
         ofp_print_meter_stats_reply(string, oh);
         break;
 
-    case OFPTYPE_METER_CONFIG_REPLY:
+    case OFPTYPE_METER_CONFIG_STATS_REPLY:
         ofp_print_stats_reply(string, oh);
         ofp_print_meter_config_reply(string, oh);
         break;
 
-    case OFPTYPE_METER_FEATURES_REPLY:
+    case OFPTYPE_METER_FEATURES_STATS_REPLY:
         ofp_print_stats_reply(string, oh);
         ofp_print_meter_features_reply(string, oh);
         break;
 
     case OFPTYPE_DESC_STATS_REQUEST:
     case OFPTYPE_PORT_DESC_STATS_REQUEST:
-    case OFPTYPE_METER_FEATURES_REQUEST:
+    case OFPTYPE_METER_FEATURES_STATS_REQUEST:
         ofp_print_stats_request(string, oh);
         break;
 
index aa4009d..bc85797 100644 (file)
@@ -547,6 +547,71 @@ ofputil_match_to_ofp11_match(const struct match *match,
     ofmatch->wildcards = htonl(wc);
 }
 
+/* Returns the "typical" length of a match for 'protocol', for use in
+ * estimating space to preallocate. */
+int
+ofputil_match_typical_len(enum ofputil_protocol protocol)
+{
+    switch (protocol) {
+    case OFPUTIL_P_OF10_STD:
+    case OFPUTIL_P_OF10_STD_TID:
+        return sizeof(struct ofp10_match);
+
+    case OFPUTIL_P_OF10_NXM:
+    case OFPUTIL_P_OF10_NXM_TID:
+        return NXM_TYPICAL_LEN;
+
+    case OFPUTIL_P_OF11_STD:
+        return sizeof(struct ofp11_match);
+
+    case OFPUTIL_P_OF12_OXM:
+    case OFPUTIL_P_OF13_OXM:
+        return NXM_TYPICAL_LEN;
+
+    default:
+        NOT_REACHED();
+    }
+}
+
+/* Appends to 'b' an struct ofp11_match_header followed by a match that
+ * expresses 'match' properly for 'protocol', plus enough zero bytes to pad the
+ * data appended out to a multiple of 8.  'protocol' must be one that is usable
+ * in OpenFlow 1.1 or later.
+ *
+ * This function can cause 'b''s data to be reallocated.
+ *
+ * Returns the number of bytes appended to 'b', excluding the padding.  Never
+ * returns zero. */
+int
+ofputil_put_ofp11_match(struct ofpbuf *b, const struct match *match,
+                        enum ofputil_protocol protocol)
+{
+    switch (protocol) {
+    case OFPUTIL_P_OF10_STD:
+    case OFPUTIL_P_OF10_STD_TID:
+    case OFPUTIL_P_OF10_NXM:
+    case OFPUTIL_P_OF10_NXM_TID:
+        NOT_REACHED();
+
+    case OFPUTIL_P_OF11_STD: {
+        struct ofp11_match *om;
+
+        /* Make sure that no padding is needed. */
+        BUILD_ASSERT_DECL(sizeof *om % 8 == 0);
+
+        om = ofpbuf_put_uninit(b, sizeof *om);
+        ofputil_match_to_ofp11_match(match, om);
+        return sizeof *om;
+    }
+
+    case OFPUTIL_P_OF12_OXM:
+    case OFPUTIL_P_OF13_OXM:
+        return oxm_put_match(b, match);
+    }
+
+    NOT_REACHED();
+}
+
 /* Given a 'dl_type' value in the format used in struct flow, returns the
  * corresponding 'dl_type' value for use in an ofp10_match or ofp11_match
  * structure. */
@@ -589,6 +654,7 @@ static const struct proto_abbrev proto_abbrevs[] = {
 enum ofputil_protocol ofputil_flow_dump_protocols[] = {
     OFPUTIL_P_OF13_OXM,
     OFPUTIL_P_OF12_OXM,
+    OFPUTIL_P_OF11_STD,
     OFPUTIL_P_OF10_NXM,
     OFPUTIL_P_OF10_STD,
 };
@@ -604,11 +670,12 @@ ofputil_protocols_from_ofp_version(enum ofp_version version)
     switch (version) {
     case OFP10_VERSION:
         return OFPUTIL_P_OF10_STD_ANY | OFPUTIL_P_OF10_NXM_ANY;
+    case OFP11_VERSION:
+        return OFPUTIL_P_OF11_STD;
     case OFP12_VERSION:
         return OFPUTIL_P_OF12_OXM;
     case OFP13_VERSION:
         return OFPUTIL_P_OF13_OXM;
-    case OFP11_VERSION:
     default:
         return 0;
     }
@@ -636,6 +703,8 @@ ofputil_protocol_to_ofp_version(enum ofputil_protocol protocol)
     case OFPUTIL_P_OF10_NXM:
     case OFPUTIL_P_OF10_NXM_TID:
         return OFP10_VERSION;
+    case OFPUTIL_P_OF11_STD:
+        return OFP11_VERSION;
     case OFPUTIL_P_OF12_OXM:
         return OFP12_VERSION;
     case OFPUTIL_P_OF13_OXM:
@@ -707,6 +776,9 @@ ofputil_protocol_set_tid(enum ofputil_protocol protocol, bool enable)
     case OFPUTIL_P_OF10_NXM_TID:
         return enable ? OFPUTIL_P_OF10_NXM_TID : OFPUTIL_P_OF10_NXM;
 
+    case OFPUTIL_P_OF11_STD:
+        return OFPUTIL_P_OF11_STD;
+
     case OFPUTIL_P_OF12_OXM:
         return OFPUTIL_P_OF12_OXM;
 
@@ -744,6 +816,9 @@ ofputil_protocol_set_base(enum ofputil_protocol cur,
     case OFPUTIL_P_OF10_NXM_TID:
         return ofputil_protocol_set_tid(OFPUTIL_P_OF10_NXM, tid);
 
+    case OFPUTIL_P_OF11_STD:
+        return ofputil_protocol_set_tid(OFPUTIL_P_OF11_STD, tid);
+
     case OFPUTIL_P_OF12_OXM:
         return ofputil_protocol_set_tid(OFPUTIL_P_OF12_OXM, tid);
 
@@ -778,6 +853,9 @@ ofputil_protocol_to_string(enum ofputil_protocol protocol)
     case OFPUTIL_P_OF10_STD_TID:
         return "OpenFlow10+table_id";
 
+    case OFPUTIL_P_OF11_STD:
+        return "OpenFlow11";
+
     case OFPUTIL_P_OF12_OXM:
         return "OXM-OpenFlow12";
 
@@ -1294,7 +1372,7 @@ ofputil_decode_hello(const struct ofp_header *oh, uint32_t *allowed_versions)
 
 /* Returns true if 'allowed_versions' needs to be accompanied by a version
  * bitmap to be correctly expressed in an OFPT_HELLO message. */
-static inline bool
+static bool
 should_send_version_bitmap(uint32_t allowed_versions)
 {
     return !is_pow2((allowed_versions >> 1) + 1);
@@ -1364,9 +1442,10 @@ ofputil_encode_set_protocol(enum ofputil_protocol current,
         case OFPUTIL_P_OF10_STD:
             return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW10);
 
+        case OFPUTIL_P_OF11_STD:
         case OFPUTIL_P_OF12_OXM:
         case OFPUTIL_P_OF13_OXM:
-            /* There are only one of each OpenFlow 1.2+ protocols and we already
+            /* There is only one variant of each OpenFlow 1.1+ protocol, and we
              * verified above that we're not trying to change versions. */
             NOT_REACHED();
 
@@ -1514,7 +1593,14 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
 
         /* Translate the message. */
         fm->priority = ntohs(ofm->priority);
-        if (ofm->command == OFPFC_ADD) {
+        if (ofm->command == OFPFC_ADD
+            || (oh->version == OFP11_VERSION
+                && (ofm->command == OFPFC_MODIFY ||
+                    ofm->command == OFPFC_MODIFY_STRICT)
+                && ofm->cookie_mask == htonll(0))) {
+            /* In OpenFlow 1.1 only, a "modify" or "modify-strict" that does
+             * not match on the cookie is treated as an "add" if there is no
+             * match. */
             fm->cookie = htonll(0);
             fm->cookie_mask = htonll(0);
             fm->new_cookie = ofm->cookie;
@@ -1523,6 +1609,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             fm->cookie_mask = ofm->cookie_mask;
             fm->new_cookie = htonll(UINT64_MAX);
         }
+        fm->modify_cookie = false;
         fm->command = ofm->command;
         fm->table_id = ofm->table_id;
         fm->idle_timeout = ntohs(ofm->idle_timeout);
@@ -1620,6 +1707,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
                     : OFPERR_OFPFMFC_TABLE_FULL);
         }
 
+        fm->modify_cookie = fm->new_cookie != htonll(UINT64_MAX);
         if (protocol & OFPUTIL_P_TID) {
             fm->command = command & 0xff;
             fm->table_id = command >> 8;
@@ -2017,15 +2105,22 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
     struct ofpbuf *msg;
 
     switch (protocol) {
+    case OFPUTIL_P_OF11_STD:
     case OFPUTIL_P_OF12_OXM:
     case OFPUTIL_P_OF13_OXM: {
         struct ofp11_flow_mod *ofm;
+        int tailroom;
 
+        tailroom = ofputil_match_typical_len(protocol) + fm->ofpacts_len;
         msg = ofpraw_alloc(OFPRAW_OFPT11_FLOW_MOD,
                            ofputil_protocol_to_ofp_version(protocol),
-                           NXM_TYPICAL_LEN + fm->ofpacts_len);
+                           tailroom);
         ofm = ofpbuf_put_zeros(msg, sizeof *ofm);
-        if (fm->command == OFPFC_ADD) {
+        if ((protocol == OFPUTIL_P_OF11_STD
+             && (fm->command == OFPFC_MODIFY ||
+                 fm->command == OFPFC_MODIFY_STRICT)
+             && fm->cookie_mask == htonll(0))
+            || fm->command == OFPFC_ADD) {
             ofm->cookie = fm->new_cookie;
         } else {
             ofm->cookie = fm->cookie;
@@ -2040,7 +2135,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
         ofm->out_port = ofputil_port_to_ofp11(fm->out_port);
         ofm->out_group = htonl(OFPG11_ANY);
         ofm->flags = htons(fm->flags);
-        oxm_put_match(msg, &fm->match);
+        ofputil_put_ofp11_match(msg, &fm->match, protocol);
         ofpacts_put_openflow11_instructions(fm->ofpacts, fm->ofpacts_len, msg);
         break;
     }
@@ -2119,8 +2214,10 @@ ofputil_flow_mod_usable_protocols(const struct ofputil_flow_mod *fms,
 
         /* Matching of the cookie is only supported through NXM or OF1.1+. */
         if (fm->cookie_mask != htonll(0)) {
-            usable_protocols &= OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-                | OFPUTIL_P_OF13_OXM;
+            usable_protocols &= (OFPUTIL_P_OF10_NXM_ANY
+                                 | OFPUTIL_P_OF11_STD
+                                 | OFPUTIL_P_OF12_OXM
+                                 | OFPUTIL_P_OF13_OXM);
         }
     }
 
@@ -2240,6 +2337,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
     enum ofpraw raw;
 
     switch (protocol) {
+    case OFPUTIL_P_OF11_STD:
     case OFPUTIL_P_OF12_OXM:
     case OFPUTIL_P_OF13_OXM: {
         struct ofp11_flow_stats_request *ofsr;
@@ -2248,14 +2346,14 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
                ? OFPRAW_OFPST11_AGGREGATE_REQUEST
                : OFPRAW_OFPST11_FLOW_REQUEST);
         msg = ofpraw_alloc(raw, ofputil_protocol_to_ofp_version(protocol),
-                          NXM_TYPICAL_LEN);
+                           ofputil_match_typical_len(protocol));
         ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
         ofsr->table_id = fsr->table_id;
         ofsr->out_port = ofputil_port_to_ofp11(fsr->out_port);
         ofsr->out_group = htonl(OFPG11_ANY);
         ofsr->cookie = fsr->cookie;
         ofsr->cookie_mask = fsr->cookie_mask;
-        oxm_put_match(msg, &fsr->match);
+        ofputil_put_ofp11_match(msg, &fsr->match, protocol);
         break;
     }
 
@@ -2731,13 +2829,15 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
     struct ofpbuf *msg;
 
     switch (protocol) {
+    case OFPUTIL_P_OF11_STD:
     case OFPUTIL_P_OF12_OXM:
     case OFPUTIL_P_OF13_OXM: {
         struct ofp12_flow_removed *ofr;
 
         msg = ofpraw_alloc_xid(OFPRAW_OFPT11_FLOW_REMOVED,
                                ofputil_protocol_to_ofp_version(protocol),
-                               htonl(0), NXM_TYPICAL_LEN);
+                               htonl(0),
+                               ofputil_match_typical_len(protocol));
         ofr = ofpbuf_put_zeros(msg, sizeof *ofr);
         ofr->cookie = fr->cookie;
         ofr->priority = htons(fr->priority);
@@ -2749,7 +2849,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
         ofr->hard_timeout = htons(fr->hard_timeout);
         ofr->packet_count = htonll(fr->packet_count);
         ofr->byte_count = htonll(fr->byte_count);
-        oxm_put_match(msg, &fr->match);
+        ofputil_put_ofp11_match(msg, &fr->match, protocol);
         break;
     }
 
index 39c81be..0385a57 100644 (file)
@@ -81,6 +81,15 @@ enum ofputil_protocol {
 #define OFPUTIL_P_OF10_STD_ANY (OFPUTIL_P_OF10_STD | OFPUTIL_P_OF10_STD_TID)
 #define OFPUTIL_P_OF10_NXM_ANY (OFPUTIL_P_OF10_NXM | OFPUTIL_P_OF10_NXM_TID)
 
+    /* OpenFlow 1.1 protocol.
+     *
+     * We only support the standard OpenFlow 1.1 flow format.
+     *
+     * OpenFlow 1.1 always operates with an equivalent of the
+     * nx_flow_mod_table_id Nicira extension enabled, so there is no "TID"
+     * variant. */
+    OFPUTIL_P_OF11_STD     = 1 << 4,
+
     /* OpenFlow 1.2+ protocols (only one variant each).
      *
      * These use the standard OpenFlow Extensible Match (OXM) flow format.
@@ -88,16 +97,17 @@ enum ofputil_protocol {
      * OpenFlow 1.2+ always operates with an equivalent of the
      * nx_flow_mod_table_id Nicira extension enabled, so there is no "TID"
      * variant. */
-    OFPUTIL_P_OF12_OXM      = 1 << 4,
-    OFPUTIL_P_OF13_OXM      = 1 << 5,
+    OFPUTIL_P_OF12_OXM      = 1 << 5,
+    OFPUTIL_P_OF13_OXM      = 1 << 6,
 #define OFPUTIL_P_ANY_OXM (OFPUTIL_P_OF12_OXM | OFPUTIL_P_OF13_OXM)
 
     /* All protocols. */
-#define OFPUTIL_P_ANY ((1 << 6) - 1)
+#define OFPUTIL_P_ANY ((1 << 7) - 1)
 
     /* Protocols in which a specific table may be specified in flow_mods. */
 #define OFPUTIL_P_TID (OFPUTIL_P_OF10_STD_TID | \
                        OFPUTIL_P_OF10_NXM_TID | \
+                       OFPUTIL_P_OF11_STD |     \
                        OFPUTIL_P_ANY_OXM)
 };
 
@@ -178,7 +188,10 @@ enum ofperr ofputil_pull_ofp11_match(struct ofpbuf *, struct match *,
                                      uint16_t *padded_match_len);
 enum ofperr ofputil_match_from_ofp11_match(const struct ofp11_match *,
                                            struct match *);
+int ofputil_put_ofp11_match(struct ofpbuf *, const struct match *,
+                            enum ofputil_protocol);
 void ofputil_match_to_ofp11_match(const struct match *, struct ofp11_match *);
+int ofputil_match_typical_len(enum ofputil_protocol);
 
 /* dl_type translation between OpenFlow and 'struct flow' format. */
 ovs_be16 ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type);
@@ -197,27 +210,36 @@ struct ofpbuf *ofputil_make_flow_mod_table_id(bool flow_mod_table_id);
 /* Protocol-independent flow_mod.
  *
  * The handling of cookies across multiple versions of OpenFlow is a bit
- * confusing.  A full description of Open vSwitch's cookie handling is
- * in the DESIGN file.  The following table shows the expected values of
- * the cookie-related fields for the different flow_mod commands in
- * OpenFlow 1.0 ("OF10") and NXM.  "<used>" and "-" indicate a value
- * that may be populated and an ignored field, respectively.
- *
- *               cookie  cookie_mask  new_cookie
- *               ======  ===========  ==========
- * OF10 Add        -          0         <used>
- * OF10 Modify     -          0         <used>
- * OF10 Delete     -          0           -
- * NXM Add         -          0         <used>
- * NXM Modify    <used>     <used>      <used>
- * NXM Delete    <used>     <used>        -
- */
+ * confusing.  See DESIGN for the details. */
 struct ofputil_flow_mod {
     struct match match;
     unsigned int priority;
+
+    /* Cookie matching.  The flow_mod affects only flows that have cookies that
+     * bitwise match 'cookie' bits in positions where 'cookie_mask has 1-bits.
+     *
+     * 'cookie_mask' should be zero for OFPFC_ADD flow_mods. */
     ovs_be64 cookie;         /* Cookie bits to match. */
     ovs_be64 cookie_mask;    /* 1-bit in each 'cookie' bit to match. */
-    ovs_be64 new_cookie;     /* New cookie to install or -1. */
+
+    /* Cookie changes.
+     *
+     * OFPFC_ADD uses 'new_cookie' as the new flow's cookie.  'new_cookie'
+     * should not be UINT64_MAX.
+     *
+     * OFPFC_MODIFY and OFPFC_MODIFY_STRICT have two cases:
+     *
+     *   - If one or more matching flows exist and 'modify_cookie' is true,
+     *     then the flow_mod changes the existing flows' cookies to
+     *     'new_cookie'.  'new_cookie' should not be UINT64_MAX.
+     *
+     *   - If no matching flow exists, 'new_cookie' is not UINT64_MAX, and
+     *     'cookie_mask' is 0, then the flow_mod adds a new flow with
+     *     'new_cookie' as its cookie.
+     */
+    ovs_be64 new_cookie;     /* New cookie to install or UINT64_MAX. */
+    bool modify_cookie;      /* Set cookie of existing flow to 'new_cookie'? */
+
     uint8_t table_id;
     uint16_t command;
     uint16_t idle_timeout;
index cc9ab3d..e852761 100644 (file)
@@ -217,11 +217,12 @@ static inline bool eth_type_mpls(ovs_be16 eth_type)
 #define ETH_TOTAL_MIN (ETH_HEADER_LEN + ETH_PAYLOAD_MIN)
 #define ETH_TOTAL_MAX (ETH_HEADER_LEN + ETH_PAYLOAD_MAX)
 #define ETH_VLAN_TOTAL_MAX (ETH_HEADER_LEN + VLAN_HEADER_LEN + ETH_PAYLOAD_MAX)
+OVS_PACKED(
 struct eth_header {
     uint8_t eth_dst[ETH_ADDR_LEN];
     uint8_t eth_src[ETH_ADDR_LEN];
     ovs_be16 eth_type;
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(ETH_HEADER_LEN == sizeof(struct eth_header));
 
 #define LLC_DSAP_SNAP 0xaa
@@ -229,27 +230,30 @@ BUILD_ASSERT_DECL(ETH_HEADER_LEN == sizeof(struct eth_header));
 #define LLC_CNTL_SNAP 3
 
 #define LLC_HEADER_LEN 3
+OVS_PACKED(
 struct llc_header {
     uint8_t llc_dsap;
     uint8_t llc_ssap;
     uint8_t llc_cntl;
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(LLC_HEADER_LEN == sizeof(struct llc_header));
 
 #define SNAP_ORG_ETHERNET "\0\0" /* The compiler adds a null byte, so
                                     sizeof(SNAP_ORG_ETHERNET) == 3. */
 #define SNAP_HEADER_LEN 5
+OVS_PACKED(
 struct snap_header {
     uint8_t snap_org[3];
     ovs_be16 snap_type;
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(SNAP_HEADER_LEN == sizeof(struct snap_header));
 
 #define LLC_SNAP_HEADER_LEN (LLC_HEADER_LEN + SNAP_HEADER_LEN)
+OVS_PACKED(
 struct llc_snap_header {
     struct llc_header llc;
     struct snap_header snap;
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(LLC_SNAP_HEADER_LEN == sizeof(struct llc_snap_header));
 
 #define VLAN_VID_MASK 0x0fff
@@ -293,13 +297,14 @@ struct vlan_header {
 BUILD_ASSERT_DECL(VLAN_HEADER_LEN == sizeof(struct vlan_header));
 
 #define VLAN_ETH_HEADER_LEN (ETH_HEADER_LEN + VLAN_HEADER_LEN)
+OVS_PACKED(
 struct vlan_eth_header {
     uint8_t veth_dst[ETH_ADDR_LEN];
     uint8_t veth_src[ETH_ADDR_LEN];
     ovs_be16 veth_type;         /* Always htons(ETH_TYPE_VLAN). */
     ovs_be16 veth_tci;          /* Lowest 12 bits are VLAN ID. */
     ovs_be16 veth_next_type;
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(VLAN_ETH_HEADER_LEN == sizeof(struct vlan_eth_header));
 
 /* MPLS related definitions */
@@ -503,6 +508,7 @@ BUILD_ASSERT_DECL(TCP_HEADER_LEN == sizeof(struct tcp_header));
 #define ARP_OP_RARP 3
 
 #define ARP_ETH_HEADER_LEN 28
+OVS_PACKED(
 struct arp_eth_header {
     /* Generic members. */
     ovs_be16 ar_hrd;           /* Hardware type. */
@@ -516,7 +522,7 @@ struct arp_eth_header {
     ovs_be32 ar_spa;           /* Sender protocol address. */
     uint8_t ar_tha[ETH_ADDR_LEN]; /* Target hardware address. */
     ovs_be32 ar_tpa;           /* Target protocol address. */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(ARP_ETH_HEADER_LEN == sizeof(struct arp_eth_header));
 
 /* The IPv6 flow label is in the lower 20 bits of the first 32-bit word. */
index ea00d26..97fc806 100644 (file)
@@ -25,6 +25,7 @@
 #include "dynamic-string.h"
 #include "fatal-signal.h"
 #include "list.h"
+#include "ovs-thread.h"
 #include "socket-util.h"
 #include "timeval.h"
 #include "vlog.h"
@@ -39,31 +40,20 @@ VLOG_DEFINE_THIS_MODULE(poll_loop);
 COVERAGE_DEFINE(poll_fd_wait);
 COVERAGE_DEFINE(poll_zero_timeout);
 
-/* An event that will wake the following call to poll_block(). */
-struct poll_waiter {
-    /* Set when the waiter is created. */
-    struct list node;           /* Element in global waiters list. */
-    int fd;                     /* File descriptor. */
-    short int events;           /* Events to wait for (POLLIN, POLLOUT). */
-    const char *where;          /* Where the waiter was created. */
+struct poll_loop {
+    /* All active poll waiters. */
+    struct pollfd *pollfds;     /* Events to pass to poll(). */
+    const char **where;         /* Where each pollfd was created. */
+    size_t n_waiters;           /* Number of elems in 'where' and 'pollfds'. */
+    size_t allocated_waiters;   /* Allocated elems in 'where' and 'pollfds'. */
 
-    /* Set only when poll_block() is called. */
-    struct pollfd *pollfd;      /* Pointer to element of the pollfds array. */
+    /* Time at which to wake up the next call to poll_block(), LLONG_MIN to
+     * wake up immediately, or LLONG_MAX to wait forever. */
+    long long int timeout_when; /* In msecs as returned by time_msec(). */
+    const char *timeout_where;  /* Where 'timeout_when' was set. */
 };
 
-/* All active poll waiters. */
-static struct list waiters = LIST_INITIALIZER(&waiters);
-
-/* Time at which to wake up the next call to poll_block(), in milliseconds as
- * returned by time_msec(), LLONG_MIN to wake up immediately, or LLONG_MAX to
- * wait forever. */
-static long long int timeout_when = LLONG_MAX;
-
-/* Location where waiter created. */
-static const char *timeout_where;
-
-static struct poll_waiter *new_waiter(int fd, short int events,
-                                      const char *where);
+static struct poll_loop *poll_loop(void);
 
 /* Registers 'fd' as waiting for the specified 'events' (which should be POLLIN
  * or POLLOUT or POLLIN | POLLOUT).  The following call to poll_block() will
@@ -75,11 +65,24 @@ static struct poll_waiter *new_waiter(int fd, short int events,
  *
  * Ordinarily the 'where' argument is supplied automatically; see poll-loop.h
  * for more information. */
-struct poll_waiter *
+void
 poll_fd_wait(int fd, short int events, const char *where)
 {
+    struct poll_loop *loop = poll_loop();
+
     COVERAGE_INC(poll_fd_wait);
-    return new_waiter(fd, events, where);
+    if (loop->n_waiters >= loop->allocated_waiters) {
+        loop->where = x2nrealloc(loop->where, &loop->allocated_waiters,
+                                 sizeof *loop->where);
+        loop->pollfds = xrealloc(loop->pollfds,
+                                 (loop->allocated_waiters
+                                  * sizeof *loop->pollfds));
+    }
+
+    loop->where[loop->n_waiters] = where;
+    loop->pollfds[loop->n_waiters].fd = fd;
+    loop->pollfds[loop->n_waiters].events = events;
+    loop->n_waiters++;
 }
 
 /* Causes the following call to poll_block() to block for no more than 'msec'
@@ -126,9 +129,10 @@ poll_timer_wait(long long int msec, const char *where)
 void
 poll_timer_wait_until(long long int when, const char *where)
 {
-    if (when < timeout_when) {
-        timeout_when = when;
-        timeout_where = where;
+    struct poll_loop *loop = poll_loop();
+    if (when < loop->timeout_when) {
+        loop->timeout_when = when;
+        loop->timeout_where = where;
     }
 }
 
@@ -156,7 +160,6 @@ poll_immediate_wake(const char *where)
 static void
 log_wakeup(const char *where, const struct pollfd *pollfd, int timeout)
 {
-    static struct vlog_rate_limit trace_rl = VLOG_RATE_LIMIT_INIT(1, 1);
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
     enum vlog_level level;
     int cpu_usage;
@@ -200,11 +203,6 @@ log_wakeup(const char *where, const struct pollfd *pollfd, int timeout)
     }
     if (cpu_usage >= 0) {
         ds_put_format(&s, " (%d%% CPU usage)", cpu_usage);
-
-        if (!vlog_should_drop(THIS_MODULE, level, &trace_rl)) {
-            ds_put_char(&s, '\n');
-            format_backtraces(&s, 2);
-        }
     }
     VLOG(level, "%s", ds_cstr(&s));
     ds_destroy(&s);
@@ -216,11 +214,7 @@ log_wakeup(const char *where, const struct pollfd *pollfd, int timeout)
 void
 poll_block(void)
 {
-    static struct pollfd *pollfds;
-    static size_t max_pollfds;
-
-    struct poll_waiter *pw, *next;
-    int n_waiters, n_pollfds;
+    struct poll_loop *loop = poll_loop();
     int elapsed;
     int retval;
 
@@ -228,70 +222,62 @@ poll_block(void)
      * poll_block. */
     fatal_signal_wait();
 
-    n_waiters = list_size(&waiters);
-    if (max_pollfds < n_waiters) {
-        max_pollfds = n_waiters;
-        pollfds = xrealloc(pollfds, max_pollfds * sizeof *pollfds);
-    }
-
-    n_pollfds = 0;
-    LIST_FOR_EACH (pw, node, &waiters) {
-        pw->pollfd = &pollfds[n_pollfds];
-        pollfds[n_pollfds].fd = pw->fd;
-        pollfds[n_pollfds].events = pw->events;
-        pollfds[n_pollfds].revents = 0;
-        n_pollfds++;
-    }
-
-    if (timeout_when == LLONG_MIN) {
+    if (loop->timeout_when == LLONG_MIN) {
         COVERAGE_INC(poll_zero_timeout);
     }
-    retval = time_poll(pollfds, n_pollfds, timeout_when, &elapsed);
+
+    retval = time_poll(loop->pollfds, loop->n_waiters,
+                       loop->timeout_when, &elapsed);
     if (retval < 0) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
         VLOG_ERR_RL(&rl, "poll: %s", ovs_strerror(-retval));
     } else if (!retval) {
-        log_wakeup(timeout_where, NULL, elapsed);
-    }
+        log_wakeup(loop->timeout_where, NULL, elapsed);
+    } else if (get_cpu_usage() > 50 || VLOG_IS_DBG_ENABLED()) {
+        size_t i;
 
-    LIST_FOR_EACH_SAFE (pw, next, node, &waiters) {
-        if (pw->pollfd->revents) {
-            log_wakeup(pw->where, pw->pollfd, 0);
+        for (i = 0; i < loop->n_waiters; i++) {
+            if (loop->pollfds[i].revents) {
+                log_wakeup(loop->where[i], &loop->pollfds[i], 0);
+            }
         }
-        poll_cancel(pw);
     }
 
-    timeout_when = LLONG_MAX;
-    timeout_where = NULL;
+    loop->timeout_when = LLONG_MAX;
+    loop->timeout_where = NULL;
+    loop->n_waiters = 0;
 
     /* Handle any pending signals before doing anything else. */
     fatal_signal_run();
 }
-
-/* Cancels the file descriptor event registered with poll_fd_wait() using 'pw',
- * the struct poll_waiter returned by that function.
- *
- * An event registered with poll_fd_wait() may be canceled from its time of
- * registration until the next call to poll_block().  At that point, the event
- * is automatically canceled by the system and its poll_waiter is freed. */
-void
-poll_cancel(struct poll_waiter *pw)
+\f
+static void
+free_poll_loop(void *loop_)
 {
-    if (pw) {
-        list_remove(&pw->node);
-        free(pw);
-    }
+    struct poll_loop *loop = loop_;
+
+    free(loop->pollfds);
+    free(loop->where);
+    free(loop);
 }
-\f
-/* Creates and returns a new poll_waiter for 'fd' and 'events'. */
-static struct poll_waiter *
-new_waiter(int fd, short int events, const char *where)
+
+static struct poll_loop *
+poll_loop(void)
 {
-    struct poll_waiter *waiter = xzalloc(sizeof *waiter);
-    ovs_assert(fd >= 0);
-    waiter->fd = fd;
-    waiter->events = events;
-    waiter->where = where;
-    list_push_back(&waiters, &waiter->node);
-    return waiter;
+    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+    static pthread_key_t key;
+    struct poll_loop *loop;
+
+    if (ovsthread_once_start(&once)) {
+        xpthread_key_create(&key, free_poll_loop);
+        ovsthread_once_done(&once);
+    }
+
+    loop = pthread_getspecific(key);
+    if (!loop) {
+        loop = xzalloc(sizeof *loop);
+        pthread_setspecific(key, loop);
+    }
+    return loop;
 }
+
index e77d027..6614ebe 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 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.
 
 /* High-level wrapper around the "poll" system call.
  *
- * Intended usage is for the program's main loop to go about its business
+ * The intended usage is for each thread's main loop to go about its business
  * servicing whatever events it needs to.  Then, when it runs out of immediate
  * tasks, it calls each subordinate module's "wait" function, which in turn
  * calls one (or more) of the functions poll_fd_wait(), poll_immediate_wake(),
  * and poll_timer_wait() to register to be awakened when the appropriate event
  * occurs.  Then the main loop calls poll_block(), which blocks until one of
- * the registered events happens. */
-
+ * the registered events happens.
+ *
+ *
+ * Thread-safety
+ * =============
+ *
+ * The poll set is per-thread, so all functions in this module are thread-safe.
+ */
 #ifndef POLL_LOOP_H
 #define POLL_LOOP_H 1
 
 extern "C" {
 #endif
 
-struct poll_waiter;
 
 /* Schedule events to wake up the following poll_block().
  *
  * The poll_loop logs the 'where' argument to each function at "debug" level
  * when an event causes a wakeup.  Ordinarily, it is automatically filled in
- * with the location in the source of the call, and caller should therefore
+ * with the location in the source of the call, and the caller should therefore
  * omit it.  But, if the function you are implementing is very generic, so that
  * its location in the source would not be very helpful for debugging, you can
  * avoid the macro expansion and pass a different argument, e.g.:
  *      (poll_fd_wait)(fd, events, where);
  * See timer_wait() for an example.
  */
-struct poll_waiter *poll_fd_wait(int fd, short int events, const char *where);
+void poll_fd_wait(int fd, short int events, const char *where);
 #define poll_fd_wait(fd, events) poll_fd_wait(fd, events, SOURCE_LOCATOR)
 
 void poll_timer_wait(long long int msec, const char *where);
@@ -62,9 +67,6 @@ void poll_immediate_wake(const char *where);
 /* Wait until an event occurs. */
 void poll_block(void);
 
-/* Cancel a file descriptor callback or event. */
-void poll_cancel(struct poll_waiter *);
-
 #ifdef  __cplusplus
 }
 #endif
index e4caaa9..39a12c9 100644 (file)
@@ -1139,14 +1139,14 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
     case OFPTYPE_GET_ASYNC_REQUEST:
     case OFPTYPE_GET_ASYNC_REPLY:
-    case OFPTYPE_GROUP_REQUEST:
-    case OFPTYPE_GROUP_REPLY:
-    case OFPTYPE_GROUP_DESC_REQUEST:
-    case OFPTYPE_GROUP_DESC_REPLY:
-    case OFPTYPE_GROUP_FEATURES_REQUEST:
-    case OFPTYPE_GROUP_FEATURES_REPLY:
-    case OFPTYPE_TABLE_FEATURES_REQUEST:
-    case OFPTYPE_TABLE_FEATURES_REPLY:
+    case OFPTYPE_GROUP_STATS_REQUEST:
+    case OFPTYPE_GROUP_STATS_REPLY:
+    case OFPTYPE_GROUP_DESC_STATS_REQUEST:
+    case OFPTYPE_GROUP_DESC_STATS_REPLY:
+    case OFPTYPE_GROUP_FEATURES_STATS_REQUEST:
+    case OFPTYPE_GROUP_FEATURES_STATS_REPLY:
+    case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
+    case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
         return false;
 
     case OFPTYPE_PACKET_IN:
@@ -1172,12 +1172,12 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_QUEUE_STATS_REPLY:
     case OFPTYPE_PORT_DESC_STATS_REQUEST:
     case OFPTYPE_PORT_DESC_STATS_REPLY:
-    case OFPTYPE_METER_REQUEST:
-    case OFPTYPE_METER_REPLY:
-    case OFPTYPE_METER_CONFIG_REQUEST:
-    case OFPTYPE_METER_CONFIG_REPLY:
-    case OFPTYPE_METER_FEATURES_REQUEST:
-    case OFPTYPE_METER_FEATURES_REPLY:
+    case OFPTYPE_METER_STATS_REQUEST:
+    case OFPTYPE_METER_STATS_REPLY:
+    case OFPTYPE_METER_CONFIG_STATS_REQUEST:
+    case OFPTYPE_METER_CONFIG_STATS_REPLY:
+    case OFPTYPE_METER_FEATURES_STATS_REQUEST:
+    case OFPTYPE_METER_FEATURES_STATS_REPLY:
     case OFPTYPE_ROLE_REQUEST:
     case OFPTYPE_ROLE_REPLY:
     case OFPTYPE_SET_FLOW_FORMAT:
index 5891ae8..d572e8c 100644 (file)
@@ -221,22 +221,13 @@ route_table_wait(void)
 static int
 route_table_reset(void)
 {
-    int error;
     struct nl_dump dump;
     struct rtgenmsg *rtmsg;
     struct ofpbuf request, reply;
-    struct nl_sock *rtnl_sock;
 
     route_map_clear();
     route_table_valid = true;
 
-    error = nl_sock_create(NETLINK_ROUTE, &rtnl_sock);
-    if (error) {
-        VLOG_WARN_RL(&rl, "failed to reset routing table, "
-                     "cannot create RTNETLINK_ROUTE socket");
-        return error;
-    }
-
     ofpbuf_init(&request, 0);
 
     nl_msg_put_nlmsghdr(&request, sizeof *rtmsg, RTM_GETROUTE, NLM_F_REQUEST);
@@ -244,7 +235,7 @@ route_table_reset(void)
     rtmsg = ofpbuf_put_zeros(&request, sizeof *rtmsg);
     rtmsg->rtgen_family = AF_INET;
 
-    nl_dump_start(&dump, rtnl_sock, &request);
+    nl_dump_start(&dump, NETLINK_ROUTE, &request);
     ofpbuf_uninit(&request);
 
     while (nl_dump_next(&dump, &reply)) {
@@ -255,10 +246,7 @@ route_table_reset(void)
         }
     }
 
-    error = nl_dump_done(&dump);
-    nl_sock_destroy(rtnl_sock);
-
-    return error;
+    return nl_dump_done(&dump);
 }
 
 
@@ -417,26 +405,19 @@ name_table_uninit(void)
 static int
 name_table_reset(void)
 {
-    int error;
     struct nl_dump dump;
     struct rtgenmsg *rtmsg;
     struct ofpbuf request, reply;
-    struct nl_sock *rtnl_sock;
 
     name_table_valid = true;
     name_map_clear();
-    error = nl_sock_create(NETLINK_ROUTE, &rtnl_sock);
-    if (error) {
-        VLOG_WARN_RL(&rl, "failed to create NETLINK_ROUTE socket");
-        return error;
-    }
 
     ofpbuf_init(&request, 0);
     nl_msg_put_nlmsghdr(&request, sizeof *rtmsg, RTM_GETLINK, NLM_F_REQUEST);
     rtmsg = ofpbuf_put_zeros(&request, sizeof *rtmsg);
     rtmsg->rtgen_family = AF_INET;
 
-    nl_dump_start(&dump, rtnl_sock, &request);
+    nl_dump_start(&dump, NETLINK_ROUTE, &request);
     ofpbuf_uninit(&request);
 
     while (nl_dump_next(&dump, &reply)) {
@@ -453,7 +434,6 @@ name_table_reset(void)
             hmap_insert(&name_map, &nn->node, hash_int(nn->ifi_index, 0));
         }
     }
-    nl_sock_destroy(rtnl_sock);
     return nl_dump_done(&dump);
 }
 
index 7bff624..0b32cb5 100644 (file)
--- a/lib/stp.c
+++ b/lib/stp.c
@@ -39,11 +39,12 @@ VLOG_DEFINE_THIS_MODULE(stp);
 #define STP_TYPE_CONFIG 0x00
 #define STP_TYPE_TCN 0x80
 
+OVS_PACKED(
 struct stp_bpdu_header {
     ovs_be16 protocol_id;       /* STP_PROTOCOL_ID. */
     uint8_t protocol_version;   /* STP_PROTOCOL_VERSION. */
     uint8_t bpdu_type;          /* One of STP_TYPE_*. */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct stp_bpdu_header) == 4);
 
 enum stp_config_bpdu_flags {
@@ -51,6 +52,7 @@ enum stp_config_bpdu_flags {
     STP_CONFIG_TOPOLOGY_CHANGE = 0x01
 };
 
+OVS_PACKED(
 struct stp_config_bpdu {
     struct stp_bpdu_header header; /* Type STP_TYPE_CONFIG. */
     uint8_t flags;                 /* STP_CONFIG_* flags. */
@@ -62,12 +64,13 @@ struct stp_config_bpdu {
     ovs_be16 max_age;              /* 8.5.1.6: Timeout for received data. */
     ovs_be16 hello_time;           /* 8.5.1.7: Time between BPDU generation. */
     ovs_be16 forward_delay;        /* 8.5.1.8: State progression delay. */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct stp_config_bpdu) == 35);
 
+OVS_PACKED(
 struct stp_tcn_bpdu {
     struct stp_bpdu_header header; /* Type STP_TYPE_TCN. */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct stp_tcn_bpdu) == 4);
 
 struct stp_timer {
index d102582..1171f32 100644 (file)
@@ -26,7 +26,6 @@
 #include "fatal-signal.h"
 #include "poll-loop.h"
 #include "socket-util.h"
-#include "stress.h"
 #include "util.h"
 #include "stream-provider.h"
 #include "stream.h"
@@ -89,38 +88,22 @@ fd_connect(struct stream *stream)
     return check_connection_completion(s->fd);
 }
 
-STRESS_OPTION(
-    stream_flaky_recv, "simulate failure of fd stream recvs",
-    100, 0, -1, 0);
-
 static ssize_t
 fd_recv(struct stream *stream, void *buffer, size_t n)
 {
     struct stream_fd *s = stream_fd_cast(stream);
     ssize_t retval;
 
-    if (STRESS(stream_flaky_recv)) {
-        return -EIO;
-    }
-
     retval = read(s->fd, buffer, n);
     return retval >= 0 ? retval : -errno;
 }
 
-STRESS_OPTION(
-    stream_flaky_send, "simulate failure of fd stream sends",
-    100, 0, -1, 0);
-
 static ssize_t
 fd_send(struct stream *stream, const void *buffer, size_t n)
 {
     struct stream_fd *s = stream_fd_cast(stream);
     ssize_t retval;
 
-    if (STRESS(stream_flaky_send)) {
-        return -EIO;
-    }
-
     retval = write(s->fd, buffer, n);
     return (retval > 0 ? retval
             : retval == 0 ? -EAGAIN
diff --git a/lib/stress-unixctl.man b/lib/stress-unixctl.man
deleted file mode 100644 (file)
index d2716a9..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-.SS "STRESS OPTION COMMANDS"
-These command manage stress options, which allow developers testing
-Open vSwitch to trigger behavior that otherwise would occur only in
-corner cases.  Developers and testers can thereby more easily discover
-bugs that would otherwise manifest only rarely or
-nondeterministically.  Stress options may cause surprising behavior
-even when they do not actually reveal bugs, so they should only be
-enabled as part of testing Open vSwitch.
-.
-.IP "\fBstress/enable\fR"
-.IQ "\fBstress/disable\fR"
-All stress options are disabled by default.  Use \fBstress/enable\fR
-to enable stress options and \fBstress/disable\fR to disable them.
-.
-.IP "\fBstress/list\fR"
-Lists and describes the available stress options and their settings in
-tabular form.  The columns in the table are:
-.RS
-.IP "NAME"
-A single-word identifier for the option, used to identify stress
-options to \fBstress/set\fR.
-.
-.IP "DESCRIPTION"
-A description for a person unfamiliar with the detailed internals of
-the code what behavior the option affects.
-.
-.IP "PERIOD"
-Currently configured trigger period.  If the stress option is
-disabled, this is \fBdisabled\fR.  Otherwise this is a number giving
-the number of occurrences of the event between activations of the
-stress option triggers.
-.
-.IP "MODE"
-If the stress option is disabled, this is \fBn/a\fR.  Otherwise it is
-\fBperiodic\fR if the stress option triggers after exactly the period,
-or \fBrandom\fR if it triggers randomly but on average after the
-number of occurrences specified by the period.
-.
-.IP "COUNTER"
-If the stress option is disabled, this is \fBn/a\fR.  Otherwise it is
-the number of occurrences of the event before the next time the stress
-option triggers.
-.
-.IP "HITS"
-The number of times that this stress option has triggered since this
-program started.
-.
-.IP "RECOMMENDED"
-A suggested period for a person unfamiliar with the internals.  It
-should put reasonable stress on the system without crippling it.
-.
-.IP "MINIMUM"
-.IQ "MAXIMUM"
-Minimum and maximum values allowed for the period.
-.
-.IP "DEFAULT"
-The default period, used when stress options have been enabled (with
-\fBstress/enable\fR) but this particular stress option has not been
-specifically configured (with \fBstress/set\fR).  It is \fBdisabled\fR
-if the option is disabled by default.  It is nonzero for options that
-can be left on at low levels without noticeable impact to the end user.
-.RE
-.
-.IP "\fBstress/set \fIoption\fR \fIperiod\fR [\fBrandom\fR|\fBperiodic\fR]"
-Sets the period at which stress \fIoption\fR triggers to
-\fIperiod\fR.  A \fIperiod\fR of 0 disables \fIoption\fR.  Specify
-\fBrandom\fR to make the option trigger randomly with an average
-period of \fIperiod\fR, or \fBperiodic\fR to trigger exactly every
-\fIperiod\fR events; the latter is the default.
-.IP
-If stress options have not been enabled with \fBstress/enable\fR, this
-command has no effect.
diff --git a/lib/stress.c b/lib/stress.c
deleted file mode 100644 (file)
index a14209e..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (c) 2010, 2011 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.
- */
-
-#include <config.h>
-#include "stress.h"
-#include <stdlib.h>
-#include <string.h>
-#include "unixctl.h"
-#include "dynamic-string.h"
-#include "random.h"
-#include "util.h"
-#include "vlog.h"
-
-VLOG_DEFINE_THIS_MODULE(stress);
-
-/* The stress options. */
-#if USE_LINKER_SECTIONS
-extern struct stress_option *__start_stress_options[];
-extern struct stress_option *__stop_stress_options[];
-#define stress_options __start_stress_options
-#define n_stress_options (__stop_stress_options - __start_stress_options)
-#else  /* !USE_LINKER_SECTIONS */
-#undef STRESS_OPTION
-#define STRESS_OPTION(NAME, DESCRIPTION, RECOMMENDED, MIN, MAX, DEFAULT) \
-        STRESS_OPTION__(NAME, DESCRIPTION, RECOMMENDED, MIN, MAX, DEFAULT);
-#include "stress.def"
-#undef STRESS_OPTION
-
-struct stress_option *stress_options[] = {
-#define STRESS_OPTION(NAME, DESCRIPTION, RECOMMENDED, MIN, MAX, DEFAULT) \
-        &stress_##NAME,
-#include "stress.def"
-#undef STRESS_OPTION
-};
-#define n_stress_options ARRAY_SIZE(stress_options)
-#endif  /* !USE_LINKER_SECTIONS */
-
-/* Enable stress options? */
-static bool stress_enabled;
-\f
-static void
-stress_reset(struct stress_option *option)
-{
-    if (!option->period || !stress_enabled) {
-        option->counter = UINT_MAX;
-    } else if (!option->random) {
-        option->counter = option->period;
-    } else if (option->period < UINT32_MAX / 2) {
-        /* Random distribution with mean of option->period. */
-        option->counter = random_uint32() % ((2 * option->period) - 1) + 1;
-    } else {
-        option->counter = random_uint32();
-    }
-}
-
-static void
-stress_enable(bool enable)
-{
-    if (stress_enabled != enable) {
-        int i;
-
-        stress_enabled = enable;
-        for (i = 0; i < n_stress_options; i++) {
-            stress_reset(stress_options[i]);
-        }
-    }
-}
-
-bool
-stress_sample_slowpath__(struct stress_option *option)
-{
-    stress_reset(option);
-    if (option->period && stress_enabled) {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-
-        option->hits++;
-        VLOG_DBG_RL(&rl, "%s hit (%llu total)", option->name, option->hits);
-
-        return true;
-    } else {
-        return false;
-    }
-}
-
-static void
-stress_set(struct stress_option *option, unsigned int period, bool random)
-{
-    if (period > option->max) {
-        period = option->max;
-    }
-    if (period < option->min) {
-        period = option->min;
-    }
-    if (period != option->period || random != option->random) {
-        option->random = random;
-        option->period = period;
-        stress_reset(option);
-    }
-}
-\f
-static void
-stress_unixctl_list(struct unixctl_conn *conn, int argc OVS_UNUSED,
-                    const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
-{
-    int i, found = 0;
-    struct ds results;
-
-    ds_init(&results);
-    ds_put_cstr(&results, "NAME (DESCRIPTION)\n");
-    ds_put_format(&results, "%11s %10s %10s %10s\n",
-                  "PERIOD", "MODE", "COUNTER", "HITS");
-    ds_put_format(&results, "%11s %10s %10s %10s\n",
-                  "RECOMMENDED", "MINIMUM", "MAXIMUM", "DEFAULT");
-    for (i = 0; i < n_stress_options; i++) {
-        struct stress_option *option = stress_options[i];
-        if (!argv[1] || strstr(option->name, argv[1])) {
-            ds_put_format(&results, "\n%s (%s)\n",
-                          option->name, option->description);
-            if (option->period) {
-                ds_put_format(&results, "%11u %10s ", option->period,
-                              option->random ? "random" : "periodic");
-                if (stress_enabled) {
-                    ds_put_format(&results, "%10u", option->counter);
-                } else {
-                    ds_put_cstr(&results, "     n/a");
-                }
-            } else {
-                ds_put_format(&results, "%11s %10s %10s",
-                              "disabled", "n/a", "n/a");
-            }
-            ds_put_format(&results, " %10llu\n", option->hits);
-            ds_put_format(&results, "%11u %10u %10u ",
-                          option->recommended, option->min, option->max);
-            if (!option->def) {
-                ds_put_format(&results, "%10s", "disabled");
-            } else {
-                ds_put_format(&results, "%10u", option->def);
-            }
-            ds_put_char(&results, '\n');
-            found++;
-        }
-    }
-    if (found) {
-        unixctl_command_reply(conn, ds_cstr(&results));
-    } else {
-        unixctl_command_reply_error(conn, NULL);
-    }
-    ds_destroy(&results);
-}
-
-static void
-stress_unixctl_enable(struct unixctl_conn *conn, int argc OVS_UNUSED,
-                      const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
-{
-    stress_enable(true);
-    unixctl_command_reply(conn, NULL);
-}
-
-static void
-stress_unixctl_disable(struct unixctl_conn *conn, int argc OVS_UNUSED,
-                       const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
-{
-    stress_enable(false);
-    unixctl_command_reply(conn, NULL);
-}
-
-static void
-stress_unixctl_set(struct unixctl_conn *conn, int argc OVS_UNUSED,
-                   const char *argv[], void *aux OVS_UNUSED)
-{
-    const char *option_name = argv[1];
-    const char *option_val = argv[2];
-    int i;
-
-    for (i = 0; i < n_stress_options; i++) {
-        struct stress_option *option = stress_options[i];
-        if (!strcmp(option_name, option->name)) {
-            unsigned int period = strtoul(option_val, NULL, 0);
-            bool random = !strcmp(argv[3], "random");
-
-            stress_set(option, period, random);
-            unixctl_command_reply(conn, NULL);
-            return;
-        }
-    }
-
-    unixctl_command_reply_error(conn, NULL);
-}
-
-/* Exposes ovs-appctl access to the stress options.
- *
- * This function is not required to simply reference stress options and have
- * them fire at their default periods.
- */
-void
-stress_init_command(void)
-{
-    unixctl_command_register("stress/list", "", 0, 1,
-                             stress_unixctl_list, NULL);
-    unixctl_command_register("stress/set", "option period [random | periodic]",
-                             2, 3, stress_unixctl_set, NULL);
-    unixctl_command_register("stress/enable", "", 0, 0,
-                             stress_unixctl_enable, NULL);
-    unixctl_command_register("stress/disable", "", 0, 0,
-                             stress_unixctl_disable, NULL);
-}
diff --git a/lib/stress.h b/lib/stress.h
deleted file mode 100644 (file)
index 8f3d359..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 2010, 2011 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.
- */
-
-#ifndef STRESS_H
-#define STRESS_H 1
-
-#include <stdbool.h>
-
-struct stress_option {
-    /* Properties. */
-    char *name;                 /* Short identifier string */
-    char *description;          /* Description of what the option stresses. */
-    unsigned int recommended;   /* Recommended period. */
-    unsigned int min;           /* Minimum period that can be set. */
-    unsigned int max;           /* Maximum period that can be set. */
-    unsigned int def;           /* Default value. */
-
-    /* Configuration. */
-    unsigned int period;        /* Desired period for firing, 0 to disable. */
-    bool random;                /* Fire randomly or exactly at period? */
-
-    /* State. */
-    unsigned int counter;       /* Number of hits before next firing. */
-    unsigned long long int hits; /* Hits since last reset. */
-};
-
-/* Creates and initializes a global instance of a stress option.
- *
- * NAME is a single word descriptive identifier for the option.  This is the
- * token to pass in to the STRESS() macro at the sites where exectution is to
- * be controlled by the option.
- *
- * DESCRIPTION is a quoted string that should describe to a person unfamiliar
- * with the detailed internals of the code what behavior the option affects.
- *
- * RECOMMENDED is a suggested value for a person unfamiliar with the internals.
- * It should put reasonable stress on the system without crippling it.
- *
- * MIN and MAX are the minimum and maximum values allowed for the option.
- *
- * DEFAULT is the default value for the option.  Specify 0 to disable the
- * option by default, which should be the usual choice.  But some options can
- * be left on at low levels without noticeable impact to the end user.  An
- * example would be failing to allocate a buffer for every 100000th packet
- * processed by the system.
- */
-#if USE_LINKER_SECTIONS
-#define STRESS_OPTION(NAME, DESCRIPTION, RECOMMENDED, MIN, MAX, DEFAULT) \
-        STRESS_OPTION__(NAME, DESCRIPTION, RECOMMENDED, MIN, MAX, DEFAULT); \
-        extern struct stress_option *stress_option_ptr_##NAME;          \
-        struct stress_option *stress_option_ptr_##NAME                  \
-            __attribute__((section("stress_options"))) = &stress_##NAME
-#else
-#define STRESS_OPTION(NAME, DESCRIPTION, RECOMMENDED, MIN, MAX, DEFAULT) \
-        extern struct stress_option stress_##NAME
-#endif
-
-/* Yields true if stress option NAME should be triggered,
- * false otherwise. */
-#define STRESS(NAME) stress_sample__(&stress_##NAME)
-
-void stress_init_command(void);
-\f
-/* Implementation details. */
-
-#define STRESS_OPTION__(NAME, DESCRIPTION, RECOMMENDED, MIN, MAX, DEFAULT) \
-        extern struct stress_option stress_##NAME;                      \
-        struct stress_option stress_##NAME =                            \
-        { #NAME, DESCRIPTION, RECOMMENDED, MIN, MAX, DEFAULT,           \
-          DEFAULT ? DEFAULT : 0,                /* period */            \
-          false,                                /* random */            \
-          UINT_MAX,                             /* counter */           \
-          0 }                                   /* hits */
-
-bool stress_sample_slowpath__(struct stress_option *);
-static inline bool stress_sample__(struct stress_option *option)
-{
-    return --option->counter == 0 && stress_sample_slowpath__(option);
-}
-
-#endif /* STRESS_H */
index 0ccfa42..4ed3ebf 100644 (file)
@@ -18,6 +18,7 @@
 #include "timeval.h"
 #include <errno.h>
 #include <poll.h>
+#include <pthread.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
 #include "util.h"
 #include "vlog.h"
 
-/* backtrace() from <execinfo.h> is really useful, but it is not signal safe
- * everywhere, such as on x86-64.  */
-#if HAVE_BACKTRACE && !defined __x86_64__
-#  define USE_BACKTRACE 1
-#  include <execinfo.h>
-#else
-#  define USE_BACKTRACE 0
-#endif
-
 VLOG_DEFINE_THIS_MODULE(timeval);
 
-/* The clock to use for measuring time intervals.  This is CLOCK_MONOTONIC by
- * preference, but on systems that don't have a monotonic clock we fall back
- * to CLOCK_REALTIME. */
-static clockid_t monotonic_clock;
+struct clock {
+    clockid_t id;               /* CLOCK_MONOTONIC or CLOCK_REALTIME. */
+    pthread_rwlock_t rwlock;    /* Mutual exclusion for 'cache'. */
 
-/* Has a timer tick occurred? Only relevant if CACHE_TIME is true.
- *
- * We initialize these to true to force time_init() to get called on the first
- * call to time_msec() or another function that queries the current time. */
-static volatile sig_atomic_t wall_tick = true;
-static volatile sig_atomic_t monotonic_tick = true;
+    /* Features for use by unit tests.  Protected by 'rwlock'. */
+    struct timespec warp;       /* Offset added for unit tests. */
+    bool stopped;               /* Disables real-time updates if true.  */
+
+    /* Relevant only if CACHE_TIME is true. */
+    volatile sig_atomic_t tick; /* Has the timer ticked?  Set by signal. */
+    struct timespec cache;      /* Last time read from kernel. */
+};
 
-/* The current time, as of the last refresh. */
-static struct timespec wall_time;
-static struct timespec monotonic_time;
+/* Our clocks. */
+static struct clock monotonic_clock; /* CLOCK_MONOTONIC, if available. */
+static struct clock wall_clock;      /* CLOCK_REALTIME. */
 
 /* The monotonic time at which the time module was initialized. */
 static long long int boot_time;
 
-/* features for use by unit tests. */
-static struct timespec warp_offset; /* Offset added to monotonic_time. */
-static bool time_stopped;           /* Disables real-time updates, if true. */
-
-/* Time in milliseconds at which to die with SIGALRM (if not LLONG_MAX). */
+/* Monotonic time in milliseconds at which to die with SIGALRM (if not
+ * LLONG_MAX). */
 static long long int deadline = LLONG_MAX;
 
-struct trace {
-    void *backtrace[32]; /* Populated by backtrace(). */
-    size_t n_frames;     /* Number of frames in 'backtrace'. */
-
-    /* format_backtraces() helper data. */
-    struct hmap_node node;
-    size_t count;
-};
-
-#define MAX_TRACES 50
-static struct trace traces[MAX_TRACES];
-static size_t trace_head = 0;
+/* Monotonic time, in milliseconds, at which the last call to time_poll() woke
+ * up. */
+DEFINE_PER_THREAD_DATA(long long int, last_wakeup, 0);
 
 static void set_up_timer(void);
 static void set_up_signal(int flags);
 static void sigalrm_handler(int);
-static void refresh_wall_if_ticked(void);
-static void refresh_monotonic_if_ticked(void);
 static void block_sigalrm(sigset_t *);
 static void unblock_sigalrm(const sigset_t *);
 static void log_poll_interval(long long int last_wakeup);
@@ -98,62 +77,39 @@ static struct rusage *get_recent_rusage(void);
 static void refresh_rusage(void);
 static void timespec_add(struct timespec *sum,
                          const struct timespec *a, const struct timespec *b);
-static unixctl_cb_func backtrace_cb;
 
-#if !USE_BACKTRACE
-static int
-backtrace(void **buffer OVS_UNUSED, int size OVS_UNUSED)
-{
-    NOT_REACHED();
-}
-
-static char **
-backtrace_symbols(void *const *buffer OVS_UNUSED, int size OVS_UNUSED)
+static void
+init_clock(struct clock *c, clockid_t id)
 {
-    NOT_REACHED();
+    memset(c, 0, sizeof *c);
+    c->id = id;
+    xpthread_rwlock_init(&c->rwlock, NULL);
+    xclock_gettime(c->id, &c->cache);
 }
-#endif  /* !USE_BACKTRACE */
 
-/* Initializes the timetracking module, if not already initialized. */
 static void
-time_init(void)
+do_init_time(void)
 {
-    static bool inited;
-
-    if (inited) {
-        return;
-    }
-    inited = true;
-
-    /* The implementation of backtrace() in glibc does some one time
-     * initialization which is not signal safe.  This can cause deadlocks if
-     * run from the signal handler.  As a workaround, force the initialization
-     * to happen here. */
-    if (USE_BACKTRACE) {
-        void *bt[1];
-
-        backtrace(bt, ARRAY_SIZE(bt));
-    }
-
-    memset(traces, 0, sizeof traces);
-
-    if (USE_BACKTRACE && CACHE_TIME) {
-        unixctl_command_register("backtrace", "", 0, 0, backtrace_cb, NULL);
-    }
+    struct timespec ts;
 
     coverage_init();
 
-    if (!clock_gettime(CLOCK_MONOTONIC, &monotonic_time)) {
-        monotonic_clock = CLOCK_MONOTONIC;
-    } else {
-        monotonic_clock = CLOCK_REALTIME;
-        VLOG_DBG("monotonic timer not available");
-    }
+    init_clock(&monotonic_clock, (!clock_gettime(CLOCK_MONOTONIC, &ts)
+                                  ? CLOCK_MONOTONIC
+                                  : CLOCK_REALTIME));
+    init_clock(&wall_clock, CLOCK_REALTIME);
+    boot_time = timespec_to_msec(&monotonic_clock.cache);
 
     set_up_signal(SA_RESTART);
     set_up_timer();
+}
 
-    boot_time = time_msec();
+/* Initializes the timetracking module, if not already initialized. */
+static void
+time_init(void)
+{
+    static pthread_once_t once = PTHREAD_ONCE_INIT;
+    pthread_once(&once, do_init_time);
 }
 
 static void
@@ -178,7 +134,7 @@ set_up_timer(void)
         return;
     }
 
-    if (timer_create(monotonic_clock, NULL, &timer_id)) {
+    if (timer_create(monotonic_clock.id, NULL, &timer_id)) {
         VLOG_FATAL("timer_create failed (%s)", ovs_strerror(errno));
     }
 
@@ -199,95 +155,107 @@ set_up_timer(void)
 void
 time_postfork(void)
 {
+    assert_single_threaded();
     time_init();
     set_up_timer();
 }
 
-static void
-refresh_wall(void)
+/* Forces a refresh of the current time from the kernel.  It is not usually
+ * necessary to call this function, since the time will be refreshed
+ * automatically at least every TIME_UPDATE_INTERVAL milliseconds.  If
+ * CACHE_TIME is false, we will always refresh the current time so this
+ * function has no effect. */
+void
+time_refresh(void)
 {
-    time_init();
-    clock_gettime(CLOCK_REALTIME, &wall_time);
-    wall_tick = false;
+    monotonic_clock.tick = wall_clock.tick = true;
 }
 
 static void
-refresh_monotonic(void)
+time_timespec__(struct clock *c, struct timespec *ts)
 {
     time_init();
-
-    if (!time_stopped) {
-        if (monotonic_clock == CLOCK_MONOTONIC) {
-            clock_gettime(monotonic_clock, &monotonic_time);
-        } else {
-            refresh_wall_if_ticked();
-            monotonic_time = wall_time;
+    for (;;) {
+        /* Use the cached time by preference, but fall through if there's been
+         * a clock tick.  */
+        xpthread_rwlock_rdlock(&c->rwlock);
+        if (c->stopped || !c->tick) {
+            timespec_add(ts, &c->cache, &c->warp);
+            xpthread_rwlock_unlock(&c->rwlock);
+            return;
         }
-        timespec_add(&monotonic_time, &monotonic_time, &warp_offset);
+        xpthread_rwlock_unlock(&c->rwlock);
 
-        monotonic_tick = false;
+        /* Refresh the cache. */
+        xpthread_rwlock_wrlock(&c->rwlock);
+        if (c->tick) {
+            c->tick = false;
+            xclock_gettime(c->id, &c->cache);
+        }
+        xpthread_rwlock_unlock(&c->rwlock);
     }
 }
 
-/* Forces a refresh of the current time from the kernel.  It is not usually
- * necessary to call this function, since the time will be refreshed
- * automatically at least every TIME_UPDATE_INTERVAL milliseconds.  If
- * CACHE_TIME is false, we will always refresh the current time so this
- * function has no effect. */
+/* Stores a monotonic timer, accurate within TIME_UPDATE_INTERVAL ms, into
+ * '*ts'. */
 void
-time_refresh(void)
+time_timespec(struct timespec *ts)
+{
+    time_timespec__(&monotonic_clock, ts);
+}
+
+/* Stores the current time, accurate within TIME_UPDATE_INTERVAL ms, into
+ * '*ts'. */
+void
+time_wall_timespec(struct timespec *ts)
 {
-    wall_tick = monotonic_tick = true;
+    time_timespec__(&wall_clock, ts);
+}
+
+static time_t
+time_sec__(struct clock *c)
+{
+    struct timespec ts;
+
+    time_timespec__(c, &ts);
+    return ts.tv_sec;
 }
 
 /* Returns a monotonic timer, in seconds. */
 time_t
 time_now(void)
 {
-    refresh_monotonic_if_ticked();
-    return monotonic_time.tv_sec;
+    return time_sec__(&monotonic_clock);
 }
 
 /* Returns the current time, in seconds. */
 time_t
 time_wall(void)
 {
-    refresh_wall_if_ticked();
-    return wall_time.tv_sec;
+    return time_sec__(&wall_clock);
+}
+
+static long long int
+time_msec__(struct clock *c)
+{
+    struct timespec ts;
+
+    time_timespec__(c, &ts);
+    return timespec_to_msec(&ts);
 }
 
 /* Returns a monotonic timer, in ms (within TIME_UPDATE_INTERVAL ms). */
 long long int
 time_msec(void)
 {
-    refresh_monotonic_if_ticked();
-    return timespec_to_msec(&monotonic_time);
+    return time_msec__(&monotonic_clock);
 }
 
 /* Returns the current time, in ms (within TIME_UPDATE_INTERVAL ms). */
 long long int
 time_wall_msec(void)
 {
-    refresh_wall_if_ticked();
-    return timespec_to_msec(&wall_time);
-}
-
-/* Stores a monotonic timer, accurate within TIME_UPDATE_INTERVAL ms, into
- * '*ts'. */
-void
-time_timespec(struct timespec *ts)
-{
-    refresh_monotonic_if_ticked();
-    *ts = monotonic_time;
-}
-
-/* Stores the current time, accurate within TIME_UPDATE_INTERVAL ms, into
- * '*ts'. */
-void
-time_wall_timespec(struct timespec *ts)
-{
-    refresh_wall_if_ticked();
-    *ts = wall_time;
+    return time_msec__(&wall_clock);
 }
 
 /* Configures the program to die with SIGALRM 'secs' seconds from now, if
@@ -325,15 +293,16 @@ int
 time_poll(struct pollfd *pollfds, int n_pollfds, long long int timeout_when,
           int *elapsed)
 {
-    static long long int last_wakeup = 0;
+    long long int *last_wakeup = last_wakeup_get();
     long long int start;
     sigset_t oldsigs;
     bool blocked;
     int retval;
 
+    time_init();
     time_refresh();
-    if (last_wakeup) {
-        log_poll_interval(last_wakeup);
+    if (*last_wakeup) {
+        log_poll_interval(*last_wakeup);
     }
     coverage_clear();
     start = time_msec();
@@ -379,41 +348,16 @@ time_poll(struct pollfd *pollfds, int n_pollfds, long long int timeout_when,
     if (blocked) {
         unblock_sigalrm(&oldsigs);
     }
-    last_wakeup = time_msec();
+    *last_wakeup = time_msec();
     refresh_rusage();
-    *elapsed = last_wakeup - start;
+    *elapsed = *last_wakeup - start;
     return retval;
 }
 
 static void
 sigalrm_handler(int sig_nr OVS_UNUSED)
 {
-    wall_tick = true;
-    monotonic_tick = true;
-
-    if (USE_BACKTRACE && CACHE_TIME) {
-        struct trace *trace = &traces[trace_head];
-
-        trace->n_frames = backtrace(trace->backtrace,
-                                    ARRAY_SIZE(trace->backtrace));
-        trace_head = (trace_head + 1) % MAX_TRACES;
-    }
-}
-
-static void
-refresh_wall_if_ticked(void)
-{
-    if (!CACHE_TIME || wall_tick) {
-        refresh_wall();
-    }
-}
-
-static void
-refresh_monotonic_if_ticked(void)
-{
-    if (!CACHE_TIME || monotonic_tick) {
-        refresh_monotonic();
-    }
+    monotonic_clock.tick = wall_clock.tick = true;
 }
 
 static void
@@ -444,7 +388,7 @@ timeval_to_msec(const struct timeval *tv)
 }
 
 /* Returns the monotonic time at which the "time" module was initialized, in
- * milliseconds(). */
+ * milliseconds. */
 long long int
 time_boot_msec(void)
 {
@@ -460,6 +404,16 @@ xgettimeofday(struct timeval *tv)
     }
 }
 
+void
+xclock_gettime(clock_t id, struct timespec *ts)
+{
+    if (clock_gettime(id, ts) == -1) {
+        /* It seems like a bad idea to try to use vlog here because it is
+         * likely to try to check the current time. */
+        ovs_abort(errno, "xclock_gettime() failed");
+    }
+}
+
 static long long int
 timeval_diff_msec(const struct timeval *a, const struct timeval *b)
 {
@@ -488,7 +442,9 @@ log_poll_interval(long long int last_wakeup)
 {
     long long int interval = time_msec() - last_wakeup;
 
-    if (interval >= 1000 && !warp_offset.tv_sec && !warp_offset.tv_nsec) {
+    if (interval >= 1000
+        && !monotonic_clock.warp.tv_sec
+        && !monotonic_clock.warp.tv_nsec) {
         const struct rusage *last_rusage = get_recent_rusage();
         struct rusage rusage;
 
@@ -529,37 +485,66 @@ struct cpu_usage {
     unsigned long long int cpu; /* Total user+system CPU usage when sampled. */
 };
 
-static struct rusage recent_rusage;
-static struct cpu_usage older = { LLONG_MIN, 0 };
-static struct cpu_usage newer = { LLONG_MIN, 0 };
-static int cpu_usage = -1;
+struct cpu_tracker {
+    struct cpu_usage older;
+    struct cpu_usage newer;
+    int cpu_usage;
+
+    struct rusage recent_rusage;
+};
+DEFINE_PER_THREAD_MALLOCED_DATA(struct cpu_tracker *, cpu_tracker_var);
+
+static struct cpu_tracker *
+get_cpu_tracker(void)
+{
+    struct cpu_tracker *t = cpu_tracker_var_get();
+    if (!t) {
+        t = xzalloc(sizeof *t);
+        t->older.when = LLONG_MIN;
+        t->newer.when = LLONG_MIN;
+        cpu_tracker_var_set_unsafe(t);
+    }
+    return t;
+}
 
 static struct rusage *
 get_recent_rusage(void)
 {
-    return &recent_rusage;
+    return &get_cpu_tracker()->recent_rusage;
+}
+
+static int
+getrusage_thread(struct rusage *rusage OVS_UNUSED)
+{
+#ifdef RUSAGE_THREAD
+    return getrusage(RUSAGE_THREAD, rusage);
+#else
+    errno = EINVAL;
+    return -1;
+#endif
 }
 
 static void
 refresh_rusage(void)
 {
-    long long int now;
+    struct cpu_tracker *t = get_cpu_tracker();
+    struct rusage *recent_rusage = &t->recent_rusage;
 
-    now = time_msec();
-    getrusage(RUSAGE_SELF, &recent_rusage);
-
-    if (now >= newer.when + 3 * 1000) {
-        older = newer;
-        newer.when = now;
-        newer.cpu = (timeval_to_msec(&recent_rusage.ru_utime) +
-                     timeval_to_msec(&recent_rusage.ru_stime));
-
-        if (older.when != LLONG_MIN && newer.cpu > older.cpu) {
-            unsigned int dividend = newer.cpu - older.cpu;
-            unsigned int divisor = (newer.when - older.when) / 100;
-            cpu_usage = divisor > 0 ? dividend / divisor : -1;
-        } else {
-            cpu_usage = -1;
+    if (!getrusage_thread(recent_rusage)) {
+        long long int now = time_msec();
+        if (now >= t->newer.when + 3 * 1000) {
+            t->older = t->newer;
+            t->newer.when = now;
+            t->newer.cpu = (timeval_to_msec(&recent_rusage->ru_utime) +
+                            timeval_to_msec(&recent_rusage->ru_stime));
+
+            if (t->older.when != LLONG_MIN && t->newer.cpu > t->older.cpu) {
+                unsigned int dividend = t->newer.cpu - t->older.cpu;
+                unsigned int divisor = (t->newer.when - t->older.when) / 100;
+                t->cpu_usage = divisor > 0 ? dividend / divisor : -1;
+            } else {
+                t->cpu_usage = -1;
+            }
         }
     }
 }
@@ -571,90 +556,7 @@ refresh_rusage(void)
 int
 get_cpu_usage(void)
 {
-    return cpu_usage;
-}
-
-static uint32_t
-hash_trace(struct trace *trace)
-{
-    return hash_bytes(trace->backtrace,
-                      trace->n_frames * sizeof *trace->backtrace, 0);
-}
-
-static struct trace *
-trace_map_lookup(struct hmap *trace_map, struct trace *key)
-{
-    struct trace *value;
-
-    HMAP_FOR_EACH_WITH_HASH (value, node, hash_trace(key), trace_map) {
-        if (key->n_frames == value->n_frames
-            && !memcmp(key->backtrace, value->backtrace,
-                       key->n_frames * sizeof *key->backtrace)) {
-            return value;
-        }
-    }
-    return NULL;
-}
-
-/*  Appends a string to 'ds' representing backtraces recorded at regular
- *  intervals in the recent past.  This information can be used to get a sense
- *  of what the process has been spending the majority of time doing.  Will
- *  ommit any backtraces which have not occurred at least 'min_count' times. */
-void
-format_backtraces(struct ds *ds, size_t min_count)
-{
-    time_init();
-
-    if (USE_BACKTRACE && CACHE_TIME) {
-        struct hmap trace_map = HMAP_INITIALIZER(&trace_map);
-        struct trace *trace, *next;
-        sigset_t oldsigs;
-        size_t i;
-
-        block_sigalrm(&oldsigs);
-
-        for (i = 0; i < MAX_TRACES; i++) {
-            struct trace *trace = &traces[i];
-            struct trace *map_trace;
-
-            if (!trace->n_frames) {
-                continue;
-            }
-
-            map_trace = trace_map_lookup(&trace_map, trace);
-            if (map_trace) {
-                map_trace->count++;
-            } else {
-                hmap_insert(&trace_map, &trace->node, hash_trace(trace));
-                trace->count = 1;
-            }
-        }
-
-        HMAP_FOR_EACH_SAFE (trace, next, node, &trace_map) {
-            char **frame_strs;
-            size_t j;
-
-            hmap_remove(&trace_map, &trace->node);
-
-            if (trace->count < min_count) {
-                continue;
-            }
-
-            frame_strs = backtrace_symbols(trace->backtrace, trace->n_frames);
-
-            ds_put_format(ds, "Count %zu\n", trace->count);
-            for (j = 0; j < trace->n_frames; j++) {
-                ds_put_format(ds, "%s\n", frame_strs[j]);
-            }
-            ds_put_cstr(ds, "\n");
-
-            free(frame_strs);
-        }
-        hmap_destroy(&trace_map);
-
-        ds_chomp(ds, '\n');
-        unblock_sigalrm(&oldsigs);
-    }
+    return get_cpu_tracker()->cpu_usage;
 }
 \f
 /* Unixctl interface. */
@@ -666,7 +568,10 @@ timeval_stop_cb(struct unixctl_conn *conn,
                  int argc OVS_UNUSED, const char *argv[] OVS_UNUSED,
                  void *aux OVS_UNUSED)
 {
-    time_stopped = true;
+    xpthread_rwlock_wrlock(&monotonic_clock.rwlock);
+    monotonic_clock.stopped = true;
+    xpthread_rwlock_unlock(&monotonic_clock.rwlock);
+
     unixctl_command_reply(conn, NULL);
 }
 
@@ -690,22 +595,12 @@ timeval_warp_cb(struct unixctl_conn *conn,
 
     ts.tv_sec = msecs / 1000;
     ts.tv_nsec = (msecs % 1000) * 1000 * 1000;
-    timespec_add(&warp_offset, &warp_offset, &ts);
-    timespec_add(&monotonic_time, &monotonic_time, &ts);
-    unixctl_command_reply(conn, "warped");
-}
 
-static void
-backtrace_cb(struct unixctl_conn *conn,
-             int argc OVS_UNUSED, const char *argv[] OVS_UNUSED,
-             void *aux OVS_UNUSED)
-{
-    struct ds ds = DS_EMPTY_INITIALIZER;
+    xpthread_rwlock_wrlock(&monotonic_clock.rwlock);
+    timespec_add(&monotonic_clock.warp, &monotonic_clock.warp, &ts);
+    xpthread_rwlock_unlock(&monotonic_clock.rwlock);
 
-    ovs_assert(USE_BACKTRACE && CACHE_TIME);
-    format_backtraces(&ds, 0);
-    unixctl_command_reply(conn, ds_cstr(&ds));
-    ds_destroy(&ds);
+    unixctl_command_reply(conn, "warped");
 }
 
 void
index eff28e2..8dd2e2b 100644 (file)
@@ -73,9 +73,9 @@ long long int timespec_to_msec(const struct timespec *);
 long long int timeval_to_msec(const struct timeval *);
 
 void xgettimeofday(struct timeval *);
+void xclock_gettime(clock_t, struct timespec *);
 
 int get_cpu_usage(void);
-void format_backtraces(struct ds *, size_t min_count);
 
 long long int time_boot_msec(void);
 
index 0ba1ed5..c69d7d1 100644 (file)
@@ -38,9 +38,9 @@ COVERAGE_DEFINE(util_xalloc);
 /* argv[0] without directory names. */
 const char *program_name;
 
-/* Ordinarily "" but set to "monitor" for a monitor process or "worker" for a
- * worker process. */
-const char *subprogram_name = "";
+/* Name for the currently running thread or process, for log messages, process
+ * listings, and debuggers. */
+DEFINE_PER_THREAD_MALLOCED_DATA(char *, subprogram_name);
 
 /* --version option output. */
 static char *program_version;
@@ -281,6 +281,7 @@ ovs_error(int err_no, const char *format, ...)
 void
 ovs_error_valist(int err_no, const char *format, va_list args)
 {
+    const char *subprogram_name = get_subprogram_name();
     int save_errno = errno;
 
     if (subprogram_name[0]) {
@@ -385,6 +386,22 @@ set_program_name__(const char *argv0, const char *version, const char *date,
     }
 }
 
+/* Returns the name of the currently running thread or process. */
+const char *
+get_subprogram_name(void)
+{
+    const char *name = subprogram_name_get();
+    return name ? name : "";
+}
+
+/* Sets 'name' as the name of the currently running thread or process.  (This
+ * appears in log messages.) */
+void
+set_subprogram_name(const char *name)
+{
+    free(subprogram_name_set(xstrdup(name)));
+}
+
 /* Returns a pointer to a string describing the program version.  The
  * caller must not modify or free the returned string.
  */
index c71f027..2159594 100644 (file)
@@ -86,7 +86,6 @@ void ovs_assert_failure(const char *, const char *, const char *) NO_RETURN;
      (TYPE) (POINTER))
 
 extern const char *program_name;
-extern const char *subprogram_name;
 
 /* Returns the number of elements in ARRAY. */
 #define ARRAY_SIZE(ARRAY) (sizeof ARRAY / sizeof *ARRAY)
@@ -109,6 +108,25 @@ is_pow2(uintmax_t x)
     return IS_POW2(x);
 }
 
+/* Returns X rounded up to a power of 2.  X must be a constant expression. */
+#define ROUND_UP_POW2(X) RUP2__(X)
+#define RUP2__(X) (RUP2_1(X) + 1)
+#define RUP2_1(X) (RUP2_2(X) | (RUP2_2(X) >> 16))
+#define RUP2_2(X) (RUP2_3(X) | (RUP2_3(X) >> 8))
+#define RUP2_3(X) (RUP2_4(X) | (RUP2_4(X) >> 4))
+#define RUP2_4(X) (RUP2_5(X) | (RUP2_5(X) >> 2))
+#define RUP2_5(X) (RUP2_6(X) | (RUP2_6(X) >> 1))
+#define RUP2_6(X) ((X) - 1)
+
+/* Returns X rounded down to a power of 2.  X must be a constant expression. */
+#define ROUND_DOWN_POW2(X) RDP2__(X)
+#define RDP2__(X) (RDP2_1(X) - (RDP2_1(X) >> 1))
+#define RDP2_1(X) (RDP2_2(X) | (RDP2_2(X) >> 16))
+#define RDP2_2(X) (RDP2_3(X) | (RDP2_3(X) >> 8))
+#define RDP2_3(X) (RDP2_4(X) | (RDP2_4(X) >> 4))
+#define RDP2_4(X) (RDP2_5(X) | (RDP2_5(X) >> 2))
+#define RDP2_5(X) (      (X) | (      (X) >> 1))
+
 #ifndef MIN
 #define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
 #endif
@@ -184,6 +202,9 @@ void set_program_name__(const char *name, const char *version,
 #define set_program_name(name) \
         set_program_name__(name, VERSION, __DATE__, __TIME__)
 
+const char *get_subprogram_name(void);
+void set_subprogram_name(const char *name);
+
 const char *get_program_version(void);
 void ovs_print_version(uint8_t min_ofp, uint8_t max_ofp);
 
index f87f48f..c2fc8e0 100644 (file)
 #include <syslog.h>
 #include <time.h>
 #include <unistd.h>
+#include "async-append.h"
 #include "coverage.h"
 #include "dirs.h"
 #include "dynamic-string.h"
 #include "ofpbuf.h"
+#include "ovs-thread.h"
 #include "sat-math.h"
 #include "svec.h"
 #include "timeval.h"
 #include "unixctl.h"
 #include "util.h"
-#include "worker.h"
 
 VLOG_DEFINE_THIS_MODULE(vlog);
 
@@ -84,7 +85,7 @@ struct vlog_module *vlog_modules[] = {
 /* Information about each facility. */
 struct facility {
     const char *name;           /* Name. */
-    char *pattern;              /* Current pattern. */
+    char *pattern;              /* Current pattern (see 'pattern_rwlock'). */
     bool default_pattern;       /* Whether current pattern is the default. */
 };
 static struct facility facilities[VLF_N_FACILITIES] = {
@@ -93,19 +94,27 @@ static struct facility facilities[VLF_N_FACILITIES] = {
 #undef VLOG_FACILITY
 };
 
-/* VLF_FILE configuration. */
+/* Protects the 'pattern' in all "struct facility"s, so that a race between
+ * changing and reading the pattern does not cause an access to freed
+ * memory. */
+static pthread_rwlock_t pattern_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+
+/* Sequence number for the message currently being composed. */
+DEFINE_PER_THREAD_DATA(unsigned int, msg_num, 0);
+
+/* VLF_FILE configuration.
+ *
+ * All of the following is protected by 'log_file_mutex', which nests inside
+ * pattern_rwlock. */
+static pthread_mutex_t log_file_mutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER;
 static char *log_file_name;
 static int log_fd = -1;
-
-/* vlog initialized? */
-static bool vlog_inited;
+static struct async_append *log_writer;
 
 static void format_log_message(const struct vlog_module *, enum vlog_level,
-                               enum vlog_facility, unsigned int msg_num,
+                               enum vlog_facility,
                                const char *message, va_list, struct ds *)
-    PRINTF_FORMAT(5, 0);
-static void vlog_write_file(struct ds *);
-static void vlog_update_async_log_fd(void);
+    PRINTF_FORMAT(4, 0);
 
 /* Searches the 'n_names' in 'names'.  Returns the index of a match for
  * 'target', or 'n_names' if no name matches. */
@@ -192,7 +201,7 @@ vlog_get_level(const struct vlog_module *module, enum vlog_facility facility)
     return module->levels[facility];
 }
 
-static void
+static void OVS_MUST_HOLD(log_file_mutex)
 update_min_level(struct vlog_module *module)
 {
     enum vlog_facility facility;
@@ -215,6 +224,7 @@ set_facility_level(enum vlog_facility facility, struct vlog_module *module,
     assert(facility >= 0 && facility < VLF_N_FACILITIES);
     assert(level < VLL_N_LEVELS);
 
+    xpthread_mutex_lock(&log_file_mutex);
     if (!module) {
         struct vlog_module **mp;
 
@@ -226,6 +236,7 @@ set_facility_level(enum vlog_facility facility, struct vlog_module *module,
         module->levels[facility] = level;
         update_min_level(module);
     }
+    xpthread_mutex_unlock(&log_file_mutex);
 }
 
 /* Sets the logging level for the given 'module' and 'facility' to 'level'.  A
@@ -249,12 +260,15 @@ static void
 do_set_pattern(enum vlog_facility facility, const char *pattern)
 {
     struct facility *f = &facilities[facility];
+
+    xpthread_rwlock_wrlock(&pattern_rwlock);
     if (!f->default_pattern) {
         free(f->pattern);
     } else {
         f->default_pattern = false;
     }
     f->pattern = xstrdup(pattern);
+    xpthread_rwlock_unlock(&pattern_rwlock);
 }
 
 /* Sets the pattern for the given 'facility' to 'pattern'. */
@@ -271,63 +285,73 @@ vlog_set_pattern(enum vlog_facility facility, const char *pattern)
     }
 }
 
-/* Returns the name of the log file used by VLF_FILE, or a null pointer if no
- * log file has been set.  (A non-null return value does not assert that the
- * named log file is in use: if vlog_set_log_file() or vlog_reopen_log_file()
- * fails, it still sets the log file name.) */
-const char *
-vlog_get_log_file(void)
-{
-    return log_file_name;
-}
-
 /* Sets the name of the log file used by VLF_FILE to 'file_name', or to the
  * default file name if 'file_name' is null.  Returns 0 if successful,
  * otherwise a positive errno value. */
 int
 vlog_set_log_file(const char *file_name)
 {
-    char *old_log_file_name;
+    char *new_log_file_name;
     struct vlog_module **mp;
-    int error;
+    struct stat old_stat;
+    struct stat new_stat;
+    int new_log_fd;
+    bool same_file;
+
+    /* Open new log file. */
+    new_log_file_name = (file_name
+                         ? xstrdup(file_name)
+                         : xasprintf("%s/%s.log", ovs_logdir(), program_name));
+    new_log_fd = open(new_log_file_name, O_WRONLY | O_CREAT | O_APPEND, 0666);
+    if (new_log_fd < 0) {
+        VLOG_WARN("failed to open %s for logging: %s",
+                  new_log_file_name, ovs_strerror(errno));
+        free(new_log_file_name);
+        return errno;
+    }
+
+    /* If the new log file is the same one we already have open, bail out. */
+    xpthread_mutex_lock(&log_file_mutex);
+    same_file = (log_fd >= 0
+                 && new_log_fd >= 0
+                 && !fstat(log_fd, &old_stat)
+                 && !fstat(new_log_fd, &new_stat)
+                 && old_stat.st_dev == new_stat.st_dev
+                 && old_stat.st_ino == new_stat.st_ino);
+    xpthread_mutex_unlock(&log_file_mutex);
+    if (same_file) {
+        close(new_log_fd);
+        free(new_log_file_name);
+        return 0;
+    }
 
-    /* Close old log file. */
+    /* Log closing old log file (we can't log while holding log_file_mutex). */
     if (log_fd >= 0) {
         VLOG_INFO("closing log file");
-        close(log_fd);
-        log_fd = -1;
     }
 
-    /* Update log file name and free old name.  The ordering is important
-     * because 'file_name' might be 'log_file_name' or some suffix of it. */
-    old_log_file_name = log_file_name;
-    log_file_name = (file_name
-                     ? xstrdup(file_name)
-                     : xasprintf("%s/%s.log", ovs_logdir(), program_name));
-    free(old_log_file_name);
-    file_name = NULL;           /* Might have been freed. */
-
-    /* Open new log file and update min_levels[] to reflect whether we actually
-     * have a log_file. */
-    log_fd = open(log_file_name, O_WRONLY | O_CREAT | O_APPEND, 0666);
+    /* Close old log file, if any, and install new one. */
+    xpthread_mutex_lock(&log_file_mutex);
     if (log_fd >= 0) {
-        vlog_update_async_log_fd();
+        free(log_file_name);
+        close(log_fd);
+        async_append_destroy(log_writer);
     }
+
+    log_file_name = xstrdup(new_log_file_name);
+    log_fd = new_log_fd;
+    log_writer = async_append_create(new_log_fd);
+
     for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) {
         update_min_level(*mp);
     }
+    xpthread_mutex_unlock(&log_file_mutex);
 
-    /* Log success or failure. */
-    if (log_fd < 0) {
-        VLOG_WARN("failed to open %s for logging: %s",
-                  log_file_name, ovs_strerror(errno));
-        error = errno;
-    } else {
-        VLOG_INFO("opened log file %s", log_file_name);
-        error = 0;
-    }
+    /* Log opening new log file (we can't log while holding log_file_mutex). */
+    VLOG_INFO("opened log file %s", new_log_file_name);
+    free(new_log_file_name);
 
-    return error;
+    return 0;
 }
 
 /* Closes and then attempts to re-open the current log file.  (This is useful
@@ -336,25 +360,19 @@ vlog_set_log_file(const char *file_name)
 int
 vlog_reopen_log_file(void)
 {
-    struct stat old, new;
+    char *fn;
 
-    /* Skip re-opening if there's nothing to reopen. */
-    if (!log_file_name) {
-        return 0;
-    }
+    xpthread_mutex_lock(&log_file_mutex);
+    fn = log_file_name ? xstrdup(log_file_name) : NULL;
+    xpthread_mutex_unlock(&log_file_mutex);
 
-    /* Skip re-opening if it would be a no-op because the old and new files are
-     * the same.  (This avoids writing "closing log file" followed immediately
-     * by "opened log file".) */
-    if (log_fd >= 0
-        && !fstat(log_fd, &old)
-        && !stat(log_file_name, &new)
-        && old.st_dev == new.st_dev
-        && old.st_ino == new.st_ino) {
+    if (fn) {
+        int error = vlog_set_log_file(fn);
+        free(fn);
+        return error;
+    } else {
         return 0;
     }
-
-    return vlog_set_log_file(log_file_name);
 }
 
 /* Set debugging levels.  Returns null if successful, otherwise an error
@@ -547,19 +565,12 @@ vlog_disable_rate_limit(struct unixctl_conn *conn, int argc,
     set_rate_limits(conn, argc, argv, false);
 }
 
-/* Initializes the logging subsystem and registers its unixctl server
- * commands. */
-void
-vlog_init(void)
+static void
+vlog_init__(void)
 {
     static char *program_name_copy;
     time_t now;
 
-    if (vlog_inited) {
-        return;
-    }
-    vlog_inited = true;
-
     /* openlog() is allowed to keep the pointer passed in, without making a
      * copy.  The daemonize code sometimes frees and replaces 'program_name',
      * so make a private copy just for openlog().  (We keep a pointer to the
@@ -587,14 +598,13 @@ vlog_init(void)
                              vlog_unixctl_reopen, NULL);
 }
 
-/* Closes the logging subsystem. */
+/* Initializes the logging subsystem and registers its unixctl server
+ * commands. */
 void
-vlog_exit(void)
+vlog_init(void)
 {
-    if (vlog_inited) {
-        closelog();
-        vlog_inited = false;
-    }
+    static pthread_once_t once = PTHREAD_ONCE_INIT;
+    pthread_once(&once, vlog_init__);
 }
 
 /* Print the current logging level for each module. */
@@ -662,7 +672,7 @@ fetch_braces(const char *p, const char *def, char *out, size_t out_size)
 
 static void
 format_log_message(const struct vlog_module *module, enum vlog_level level,
-                   enum vlog_facility facility, unsigned int msg_num,
+                   enum vlog_facility facility,
                    const char *message, va_list args_, struct ds *s)
 {
     char tmp[128];
@@ -671,6 +681,7 @@ format_log_message(const struct vlog_module *module, enum vlog_level level,
 
     ds_clear(s);
     for (p = facilities[facility].pattern; *p != '\0'; ) {
+        const char *subprogram_name;
         enum { LEFT, RIGHT } justify = RIGHT;
         int pad = '0';
         size_t length, field, used;
@@ -723,7 +734,7 @@ format_log_message(const struct vlog_module *module, enum vlog_level level,
             }
             break;
         case 'N':
-            ds_put_format(s, "%u", msg_num);
+            ds_put_format(s, "%u", *msg_num_get_unsafe());
             break;
         case 'n':
             ds_put_char(s, '\n');
@@ -738,9 +749,11 @@ format_log_message(const struct vlog_module *module, enum vlog_level level,
             ds_put_format(s, "%lld", time_msec() - time_boot_msec());
             break;
         case 't':
+            subprogram_name = get_subprogram_name();
             ds_put_cstr(s, subprogram_name[0] ? subprogram_name : "main");
             break;
         case 'T':
+            subprogram_name = get_subprogram_name();
             if (subprogram_name[0]) {
                 ds_put_format(s, "(%s)", subprogram_name);
             }
@@ -776,18 +789,17 @@ vlog_valist(const struct vlog_module *module, enum vlog_level level,
     bool log_to_file = module->levels[VLF_FILE] >= level && log_fd >= 0;
     if (log_to_console || log_to_syslog || log_to_file) {
         int save_errno = errno;
-        static unsigned int msg_num;
         struct ds s;
 
         vlog_init();
 
         ds_init(&s);
         ds_reserve(&s, 1024);
-        msg_num++;
+        ++*msg_num_get();
 
+        xpthread_rwlock_rdlock(&pattern_rwlock);
         if (log_to_console) {
-            format_log_message(module, level, VLF_CONSOLE, msg_num,
-                               message, args, &s);
+            format_log_message(module, level, VLF_CONSOLE, message, args, &s);
             ds_put_char(&s, '\n');
             fputs(ds_cstr(&s), stderr);
         }
@@ -797,8 +809,7 @@ vlog_valist(const struct vlog_module *module, enum vlog_level level,
             char *save_ptr = NULL;
             char *line;
 
-            format_log_message(module, level, VLF_SYSLOG, msg_num,
-                               message, args, &s);
+            format_log_message(module, level, VLF_SYSLOG, message, args, &s);
             for (line = strtok_r(s.string, "\n", &save_ptr); line;
                  line = strtok_r(NULL, "\n", &save_ptr)) {
                 syslog(syslog_level, "%s", line);
@@ -806,11 +817,19 @@ vlog_valist(const struct vlog_module *module, enum vlog_level level,
         }
 
         if (log_to_file) {
-            format_log_message(module, level, VLF_FILE, msg_num,
-                               message, args, &s);
+            format_log_message(module, level, VLF_FILE, message, args, &s);
             ds_put_char(&s, '\n');
-            vlog_write_file(&s);
+
+            xpthread_mutex_lock(&log_file_mutex);
+            if (log_fd >= 0) {
+                async_append_write(log_writer, s.string, s.length);
+                if (level == VLL_EMER) {
+                    async_append_flush(log_writer);
+                }
+            }
+            xpthread_mutex_unlock(&log_file_mutex);
         }
+        xpthread_rwlock_unlock(&pattern_rwlock);
 
         ds_destroy(&s);
         errno = save_errno;
@@ -910,6 +929,7 @@ vlog_should_drop(const struct vlog_module *module, enum vlog_level level,
         return true;
     }
 
+    xpthread_mutex_lock(&rl->mutex);
     if (!token_bucket_withdraw(&rl->token_bucket, VLOG_MSG_TOKENS)) {
         time_t now = time_now();
         if (!rl->n_dropped) {
@@ -917,21 +937,26 @@ vlog_should_drop(const struct vlog_module *module, enum vlog_level level,
         }
         rl->last_dropped = now;
         rl->n_dropped++;
+        xpthread_mutex_unlock(&rl->mutex);
         return true;
     }
 
-    if (rl->n_dropped) {
+    if (!rl->n_dropped) {
+        xpthread_mutex_unlock(&rl->mutex);
+    } else {
         time_t now = time_now();
+        unsigned int n_dropped = rl->n_dropped;
         unsigned int first_dropped_elapsed = now - rl->first_dropped;
         unsigned int last_dropped_elapsed = now - rl->last_dropped;
+        rl->n_dropped = 0;
+        xpthread_mutex_unlock(&rl->mutex);
 
         vlog(module, level,
              "Dropped %u log messages in last %u seconds (most recently, "
              "%u seconds ago) due to excessive rate",
-             rl->n_dropped, first_dropped_elapsed, last_dropped_elapsed);
-
-        rl->n_dropped = 0;
+             n_dropped, first_dropped_elapsed, last_dropped_elapsed);
     }
+
     return false;
 }
 
@@ -958,59 +983,3 @@ vlog_usage(void)
            "                          (default: %s/%s.log)\n",
            ovs_logdir(), program_name);
 }
-\f
-static bool vlog_async_inited = false;
-
-static worker_request_func vlog_async_write_request_cb;
-
-static void
-vlog_write_file(struct ds *s)
-{
-    if (worker_is_running()) {
-        static bool in_worker_request = false;
-        if (!in_worker_request) {
-            in_worker_request = true;
-
-            worker_request(s->string, s->length,
-                           &log_fd, vlog_async_inited ? 0 : 1,
-                           vlog_async_write_request_cb, NULL, NULL);
-            vlog_async_inited = true;
-
-            in_worker_request = false;
-            return;
-        } else {
-            /* We've been entered recursively.  This can happen if
-             * worker_request(), or a function that it calls, tries to log
-             * something.  We can't call worker_request() recursively, so fall
-             * back to writing the log file directly. */
-            COVERAGE_INC(vlog_recursive);
-        }
-    }
-    ignore(write(log_fd, s->string, s->length));
-}
-
-static void
-vlog_update_async_log_fd(void)
-{
-    if (worker_is_running()) {
-        worker_request(NULL, 0, &log_fd, 1, vlog_async_write_request_cb,
-                       NULL, NULL);
-        vlog_async_inited = true;
-    }
-}
-
-static void
-vlog_async_write_request_cb(struct ofpbuf *request,
-                            const int *fd, size_t n_fds)
-{
-    if (n_fds > 0) {
-        if (log_fd >= 0) {
-            close(log_fd);
-        }
-        log_fd = *fd;
-    }
-
-    if (request->size > 0) {
-        ignore(write(log_fd, request->data, request->size));
-    }
-}
index c7ab206..9576687 100644 (file)
 #ifndef VLOG_H
 #define VLOG_H 1
 
+/* Logging.
+ *
+ *
+ * Thread-safety
+ * =============
+ *
+ * Fully thread safe.
+ */
+
 #include <limits.h>
 #include <stdarg.h>
 #include <stdbool.h>
 #include <time.h>
 #include "compiler.h"
+#include "ovs-thread.h"
 #include "sat-math.h"
 #include "token-bucket.h"
 #include "util.h"
@@ -94,6 +104,7 @@ struct vlog_rate_limit {
     time_t first_dropped;       /* Time first message was dropped. */
     time_t last_dropped;        /* Time of most recent message drop. */
     unsigned int n_dropped;     /* Number of messages dropped. */
+    pthread_mutex_t mutex;      /* Mutual exclusion for rate limit. */
 };
 
 /* Number of tokens to emit a message.  We add 'rate' tokens per millisecond,
@@ -108,6 +119,7 @@ struct vlog_rate_limit {
             0,                              /* first_dropped */         \
             0,                              /* last_dropped */          \
             0,                              /* n_dropped */             \
+            PTHREAD_ADAPTIVE_MUTEX_INITIALIZER /* mutex */              \
         }
 
 /* Configuring how each module logs messages. */
@@ -124,13 +136,11 @@ void vlog_set_verbosity(const char *arg);
 
 /* Configuring log facilities. */
 void vlog_set_pattern(enum vlog_facility, const char *pattern);
-const char *vlog_get_log_file(void);
 int vlog_set_log_file(const char *file_name);
 int vlog_reopen_log_file(void);
 
 /* Initialization. */
 void vlog_init(void);
-void vlog_exit(void);
 
 /* Functions for actual logging. */
 void vlog(const struct vlog_module *, enum vlog_level, const char *format, ...)
@@ -230,13 +240,13 @@ void vlog_usage(void);
             vlog_rate_limit(THIS_MODULE, level__, RL, __VA_ARGS__); \
         }                                                           \
     } while (0)
-#define VLOG_ONCE(LEVEL, ...)                       \
-    do {                                            \
-        static bool already_logged;                 \
-        if (!already_logged) {                      \
-            already_logged = true;                  \
-            vlog(THIS_MODULE, LEVEL, __VA_ARGS__);  \
-        }                                           \
+#define VLOG_ONCE(LEVEL, ...)                                           \
+    do {                                                                \
+        static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; \
+        if (ovsthread_once_start(&once)) {                              \
+            vlog(THIS_MODULE, LEVEL, __VA_ARGS__);                      \
+            ovsthread_once_done(&once);                                 \
+        }                                                               \
     } while (0)
 
 #define VLOG_DEFINE_MODULE__(MODULE)                                    \
diff --git a/lib/worker.c b/lib/worker.c
deleted file mode 100644 (file)
index 6904fdd..0000000
+++ /dev/null
@@ -1,472 +0,0 @@
-/* Copyright (c) 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.
- * 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.
- */
-
-#include <config.h>
-
-#include "worker.h"
-
-#include <assert.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include "command-line.h"
-#include "daemon.h"
-#include "ofpbuf.h"
-#include "poll-loop.h"
-#include "socket-util.h"
-#include "util.h"
-#include "vlog.h"
-
-VLOG_DEFINE_THIS_MODULE(worker);
-
-/* ovs_assert() logs the assertion message and logging sometimes goes through a
- * worker, so using ovs_assert() in this source file could cause recursion. */
-#undef ovs_assert
-#define ovs_assert use_assert_instead_of_ovs_assert_in_this_module
-
-/* Header for an RPC request. */
-struct worker_request {
-    size_t request_len;              /* Length of the payload in bytes. */
-    worker_request_func *request_cb; /* Function to call in worker process. */
-    worker_reply_func *reply_cb;     /* Function to call in main process. */
-    void *reply_aux;                 /* Auxiliary data for 'reply_cb'. */
-};
-
-/* Header for an RPC reply. */
-struct worker_reply {
-    size_t reply_len;            /* Length of the payload in bytes. */
-    worker_reply_func *reply_cb; /* Function to call in main process. */
-    void *reply_aux;             /* Auxiliary data for 'reply_cb'. */
-};
-
-/* Receive buffer for a RPC request or reply. */
-struct rxbuf {
-    /* Header. */
-    struct ofpbuf header;       /* Header data. */
-    int fds[SOUTIL_MAX_FDS];    /* File descriptors. */
-    size_t n_fds;
-
-    /* Payload. */
-    struct ofpbuf payload;      /* Payload data. */
-};
-
-static int client_sock = -1;
-static struct rxbuf client_rx;
-
-static void rxbuf_init(struct rxbuf *);
-static void rxbuf_clear(struct rxbuf *);
-static int rxbuf_run(struct rxbuf *, int sock, size_t header_len);
-
-static struct iovec *prefix_iov(void *data, size_t len,
-                                const struct iovec *iovs, size_t n_iovs);
-
-static void worker_broke(void);
-
-static void worker_main(int fd) NO_RETURN;
-
-/* Starts a worker process as a subprocess of the current process.  Currently
- * only a single worker process is supported, so this function may only be
- * called once.
- *
- * The client should call worker_run() and worker_wait() from its main loop.
- *
- * Call this function between daemonize_start() and daemonize_complete(). */
-void
-worker_start(void)
-{
-    int work_fds[2];
-
-    assert(client_sock < 0);
-
-    /* Create non-blocking socket pair. */
-    xsocketpair(AF_UNIX, SOCK_STREAM, 0, work_fds);
-    xset_nonblocking(work_fds[0]);
-    xset_nonblocking(work_fds[1]);
-
-    /* Don't let the worker process own the responsibility to delete
-     * the pidfile.  Register it again after the fork. */
-    remove_pidfile_from_unlink();
-    if (!fork_and_clean_up()) {
-        /* In child (worker) process. */
-        daemonize_post_detach();
-        close(work_fds[0]);
-        worker_main(work_fds[1]);
-        NOT_REACHED();
-    }
-
-    /* In parent (main) process. */
-    add_pidfile_to_unlink();
-    close(work_fds[1]);
-    client_sock = work_fds[0];
-    rxbuf_init(&client_rx);
-}
-
-/* Returns true if this process has started a worker and the worker is not
- * known to have malfunctioned. */
-bool
-worker_is_running(void)
-{
-    return client_sock >= 0;
-}
-
-/* If a worker process was started, processes RPC replies from it, calling the
- * registered 'reply_cb' callbacks.
- *
- * If the worker process died or malfunctioned, aborts. */
-void
-worker_run(void)
-{
-    if (worker_is_running()) {
-        int error;
-
-        error = rxbuf_run(&client_rx, client_sock,
-                          sizeof(struct worker_reply));
-        if (!error) {
-            struct worker_reply *reply = client_rx.header.data;
-            reply->reply_cb(&client_rx.payload, client_rx.fds,
-                            client_rx.n_fds, reply->reply_aux);
-            rxbuf_clear(&client_rx);
-        } else if (error != EAGAIN) {
-            worker_broke();
-            VLOG_ABORT("receive from worker failed (%s)",
-                       ovs_retval_to_string(error));
-        }
-    }
-}
-
-/* Causes the poll loop to wake up if we need to process RPC replies. */
-void
-worker_wait(void)
-{
-    if (worker_is_running()) {
-        poll_fd_wait(client_sock, POLLIN);
-    }
-}
-\f
-/* Interface for main process to interact with the worker. */
-
-/* Sends an RPC request to the worker process.  The worker process will call
- * 'request_cb' passing the 'size' (zero or more) bytes of data in 'data' as
- * arguments as well as the 'n_fds' (SOUTIL_MAX_FDS or fewer) file descriptors
- * in 'fds'.
- *
- * If and only if 'reply_cb' is nonnull, 'request_cb' must call worker_reply()
- * or worker_reply_iovec() with a reply.  The main process will later call
- * 'reply_cb' with the reply data (if any) and file descriptors (if any).
- *
- * 'request_cb' receives copies (as if by dup()) of the file descriptors in
- * fds[].  'request_cb' takes ownership of these copies, and the caller of
- * worker_request() retains its ownership of the originals.
- *
- * This function may block until the RPC request has been sent (if the socket
- * buffer fills up) but it does not wait for the reply (if any).  If this
- * function blocks, it may invoke reply callbacks for previous requests.
- *
- * The worker process executes RPC requests in strict order of submission and
- * runs each request to completion before beginning the next request.  The main
- * process invokes reply callbacks in strict order of request submission. */
-void
-worker_request(const void *data, size_t size,
-               const int fds[], size_t n_fds,
-               worker_request_func *request_cb,
-               worker_reply_func *reply_cb, void *aux)
-{
-    if (size > 0) {
-        struct iovec iov;
-
-        iov.iov_base = (void *) data;
-        iov.iov_len = size;
-        worker_request_iovec(&iov, 1, fds, n_fds, request_cb, reply_cb, aux);
-    } else {
-        worker_request_iovec(NULL, 0, fds, n_fds, request_cb, reply_cb, aux);
-    }
-}
-
-static int
-worker_send_iovec(const struct iovec iovs[], size_t n_iovs,
-                  const int fds[], size_t n_fds)
-{
-    size_t sent = 0;
-
-    for (;;) {
-        struct pollfd pfd;
-        int error;
-
-        /* Try to send the rest of the request. */
-        error = send_iovec_and_fds_fully(client_sock, iovs, n_iovs,
-                                         fds, n_fds, sent, &sent);
-        if (error != EAGAIN) {
-            return error;
-        }
-
-        /* Process replies to avoid deadlock. */
-        worker_run();
-
-        /* Wait for 'client_sock' to become ready before trying again.  We
-         * can't use poll_block() because it sometimes calls into vlog, which
-         * calls indirectly into worker_send_iovec().  To be usable here,
-         * poll_block() would therefore need to be reentrant, but it isn't
-         * (calling it recursively causes memory corruption and an eventual
-         * crash). */
-        pfd.fd = client_sock;
-        pfd.events = POLLIN | POLLOUT;
-        do {
-            error = poll(&pfd, 1, -1) < 0 ? errno : 0;
-        } while (error == EINTR);
-        if (error) {
-            worker_broke();
-            VLOG_ABORT("poll failed (%s)", ovs_strerror(error));
-        }
-    }
-}
-
-/* Same as worker_request() except that the data to send is specified as an
- * array of iovecs. */
-void
-worker_request_iovec(const struct iovec iovs[], size_t n_iovs,
-                     const int fds[], size_t n_fds,
-                     worker_request_func *request_cb,
-                     worker_reply_func *reply_cb, void *aux)
-{
-    static bool recursing = false;
-    struct worker_request rq;
-    struct iovec *all_iovs;
-    int error;
-
-    assert(worker_is_running());
-    assert(!recursing);
-    recursing = true;
-
-    rq.request_len = iovec_len(iovs, n_iovs);
-    rq.request_cb = request_cb;
-    rq.reply_cb = reply_cb;
-    rq.reply_aux = aux;
-
-    all_iovs = prefix_iov(&rq, sizeof rq, iovs, n_iovs);
-    error = worker_send_iovec(all_iovs, n_iovs + 1, fds, n_fds);
-    if (error) {
-        worker_broke();
-        VLOG_ABORT("send failed (%s)", ovs_strerror(error));
-    }
-    free(all_iovs);
-
-    recursing = false;
-}
-
-/* Closes the client socket, if any, so that worker_is_running() will return
- * false.
- *
- * The client does this just before aborting if the worker process dies or
- * malfunctions, to prevent the logging subsystem from trying to use the
- * worker to log the failure. */
-static void
-worker_broke(void)
-{
-    if (client_sock >= 0) {
-        close(client_sock);
-        client_sock = -1;
-    }
-}
-\f
-/* Interfaces for RPC implementations (running in the worker process). */
-
-static int server_sock = -1;
-static bool expect_reply;
-static struct worker_request request;
-
-/* When a call to worker_request() or worker_request_iovec() provides a
- * 'reply_cb' callback, the 'request_cb' implementation must call this function
- * to send its reply.  The main process will call 'reply_cb' passing the
- * 'size' (zero or more) bytes of data in 'data' as arguments as well as the
- * 'n_fds' (SOUTIL_MAX_FDS or fewer) file descriptors in 'fds'.
- *
- * If a call to worker_request() or worker_request_iovec() provides no
- * 'reply_cb' callback, the 'request_cb' implementation must not call this
- * function.
- *
- * 'reply_cb' receives copies (as if by dup()) of the file descriptors in
- * fds[].  'reply_cb' takes ownership of these copies, and the caller of
- * worker_reply() retains its ownership of the originals.
- *
- * This function blocks until the RPC reply has been sent (if the socket buffer
- * fills up) but it does not wait for the main process to receive or to process
- * the reply. */
-void
-worker_reply(const void *data, size_t size, const int fds[], size_t n_fds)
-{
-    if (size > 0) {
-        struct iovec iov;
-
-        iov.iov_base = (void *) data;
-        iov.iov_len = size;
-        worker_reply_iovec(&iov, 1, fds, n_fds);
-    } else {
-        worker_reply_iovec(NULL, 0, fds, n_fds);
-    }
-}
-
-/* Same as worker_reply() except that the data to send is specified as an array
- * of iovecs. */
-void
-worker_reply_iovec(const struct iovec *iovs, size_t n_iovs,
-                       const int fds[], size_t n_fds)
-{
-    struct worker_reply reply;
-    struct iovec *all_iovs;
-    int error;
-
-    assert(expect_reply);
-    expect_reply = false;
-
-    reply.reply_len = iovec_len(iovs, n_iovs);
-    reply.reply_cb = request.reply_cb;
-    reply.reply_aux = request.reply_aux;
-
-    all_iovs = prefix_iov(&reply, sizeof reply, iovs, n_iovs);
-
-    error = send_iovec_and_fds_fully_block(server_sock, all_iovs, n_iovs + 1,
-                                           fds, n_fds);
-    if (error == EPIPE) {
-        /* Parent probably died.  Continue processing any RPCs still buffered,
-         * to avoid missing log messages. */
-        VLOG_INFO("send failed (%s)", ovs_strerror(error));
-    } else if (error) {
-        VLOG_FATAL("send failed (%s)", ovs_strerror(error));
-    }
-
-    free(all_iovs);
-}
-
-static void
-worker_main(int fd)
-{
-    struct rxbuf rx;
-
-    server_sock = fd;
-
-    subprogram_name = "worker";
-    proctitle_set("worker process for pid %lu", (unsigned long int) getppid());
-    VLOG_INFO("worker process started");
-
-    rxbuf_init(&rx);
-    for (;;) {
-        int error;
-
-        error = rxbuf_run(&rx, server_sock, sizeof(struct worker_request));
-        if (!error) {
-            request = *(struct worker_request *) rx.header.data;
-
-            expect_reply = request.reply_cb != NULL;
-            request.request_cb(&rx.payload, rx.fds, rx.n_fds);
-            assert(!expect_reply);
-
-            rxbuf_clear(&rx);
-        } else if (error == EOF && !rx.header.size) {
-            /* Main process closed the IPC socket.  Exit cleanly. */
-            break;
-        } else if (error != EAGAIN) {
-            VLOG_FATAL("RPC receive failed (%s)", ovs_retval_to_string(error));
-        }
-
-        poll_fd_wait(server_sock, POLLIN);
-        poll_block();
-    }
-
-    VLOG_INFO("worker process exiting");
-    exit(0);
-}
-\f
-static void
-rxbuf_init(struct rxbuf *rx)
-{
-    ofpbuf_init(&rx->header, 0);
-    rx->n_fds = 0;
-    ofpbuf_init(&rx->payload, 0);
-}
-
-static void
-rxbuf_clear(struct rxbuf *rx)
-{
-    ofpbuf_clear(&rx->header);
-    rx->n_fds = 0;
-    ofpbuf_clear(&rx->payload);
-}
-
-static int
-rxbuf_run(struct rxbuf *rx, int sock, size_t header_len)
-{
-    for (;;) {
-        if (!rx->header.size) {
-            int retval;
-
-            ofpbuf_clear(&rx->header);
-            ofpbuf_prealloc_tailroom(&rx->header, header_len);
-
-            retval = recv_data_and_fds(sock, rx->header.data, header_len,
-                                       rx->fds, &rx->n_fds);
-            if (retval <= 0) {
-                return retval ? -retval : EOF;
-            }
-            rx->header.size += retval;
-        } else if (rx->header.size < header_len) {
-            size_t bytes_read;
-            int error;
-
-            error = read_fully(sock, ofpbuf_tail(&rx->header),
-                               header_len - rx->header.size, &bytes_read);
-            rx->header.size += bytes_read;
-            if (error) {
-                return error;
-            }
-        } else {
-            size_t payload_len = *(size_t *) rx->header.data;
-
-            if (rx->payload.size < payload_len) {
-                size_t left = payload_len - rx->payload.size;
-                size_t bytes_read;
-                int error;
-
-                ofpbuf_prealloc_tailroom(&rx->payload, left);
-                error = read_fully(sock, ofpbuf_tail(&rx->payload), left,
-                                   &bytes_read);
-                rx->payload.size += bytes_read;
-                if (error) {
-                    return error;
-                }
-            } else {
-                return 0;
-            }
-        }
-    }
-}
-
-static struct iovec *
-prefix_iov(void *data, size_t len, const struct iovec *iovs, size_t n_iovs)
-{
-    struct iovec *dst;
-
-    dst = xmalloc((n_iovs + 1) * sizeof *dst);
-    dst[0].iov_base = data;
-    dst[0].iov_len = len;
-    memcpy(dst + 1, iovs, n_iovs * sizeof *iovs);
-
-    return dst;
-}
diff --git a/lib/worker.h b/lib/worker.h
deleted file mode 100644 (file)
index 135d50d..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/* Copyright (c) 2012 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.
- */
-
-#ifndef WORKER_H
-#define WORKER_H 1
-
-/* Worker processes.
- *
- * Thes functions allow an OVS daemon to fork off a "worker process" to do
- * tasks that may unavoidably block in the kernel.  The worker executes remote
- * procedure calls on behalf of the main process.
- *
- * Tasks that may unavoidably block in the kernel include writes to regular
- * files, sends to Generic Netlink sockets (which as of this writing use a
- * global lock), and other unusual operations.
- *
- * The worker functions *will* block if the finite buffer between a main
- * process and its worker process fills up.
- */
-
-#include <stdbool.h>
-#include <stddef.h>
-#include "compiler.h"
-
-struct iovec;
-struct ofpbuf;
-
-/* The main process calls this function to start a worker. */
-void worker_start(void);
-
-/* Interface for main process to interact with the worker. */
-typedef void worker_request_func(struct ofpbuf *request,
-                                 const int fds[], size_t n_fds);
-typedef void worker_reply_func(struct ofpbuf *reply,
-                               const int fds[], size_t n_fds, void *aux);
-
-bool worker_is_running(void);
-void worker_run(void);
-void worker_wait(void);
-
-void worker_request(const void *data, size_t size,
-                    const int fds[], size_t n_fds,
-                    worker_request_func *request_cb,
-                    worker_reply_func *reply_cb, void *aux);
-void worker_request_iovec(const struct iovec *iovs, size_t n_iovs,
-                          const int fds[], size_t n_fds,
-                          worker_request_func *request_cb,
-                          worker_reply_func *reply_cb, void *aux);
-
-/* Interfaces for RPC implementations (running in the worker process). */
-void worker_reply(const void *data, size_t size,
-                  const int fds[], size_t n_fds);
-void worker_reply_iovec(const struct iovec *iovs, size_t n_iovs,
-                        const int fds[], size_t n_fds);
-
-#endif /* worker.h */
index ca80506..dbfc7c4 100644 (file)
@@ -446,3 +446,8 @@ AC_DEFUN([OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE],
         __atomic_always_lock_free($1, 0).  If the C compiler is not GCC or is
         an older version of GCC, the value does not matter.])
    fi])
+
+dnl OVS_CHECK_POSIX_AIO
+AC_DEFUN([OVS_CHECK_POSIX_AIO],
+  [AC_SEARCH_LIBS([aio_write], [rt])
+   AM_CONDITIONAL([HAVE_POSIX_AIO], [test "$ac_cv_search_aio_write" != no])])
index ed0d999..263f2ea 100644 (file)
@@ -42,7 +42,6 @@ ovsdb/ovsdb-server.1: \
        lib/ssl-bootstrap.man \
        lib/ssl-syn.man \
        lib/ssl.man \
-       lib/stress-unixctl.man \
        lib/unixctl-syn.man \
        lib/unixctl.man \
        lib/vlog-syn.man \
@@ -61,7 +60,6 @@ lib/ssl-bootstrap-syn.man:
 lib/ssl-bootstrap.man:
 lib/ssl-syn.man:
 lib/ssl.man:
-lib/stress-unixctl.man:
 lib/unixctl-syn.man:
 lib/unixctl.man:
 lib/vlog-syn.man:
@@ -232,7 +230,6 @@ vswitchd/ovs-vswitchd.8: \
        lib/memory-unixctl.man \
        lib/ssl-bootstrap.man \
        lib/ssl.man \
-       lib/stress-unixctl.man \
        lib/vlog-unixctl.man \
        lib/vlog.man \
        ofproto/ofproto-dpif-unixctl.man \
@@ -246,7 +243,6 @@ lib/daemon.man:
 lib/memory-unixctl.man:
 lib/ssl-bootstrap.man:
 lib/ssl.man:
-lib/stress-unixctl.man:
 lib/vlog-unixctl.man:
 lib/vlog.man:
 ofproto/ofproto-dpif-unixctl.man:
index b4d0876..af9a12a 100644 (file)
@@ -26,6 +26,8 @@ ofproto_libofproto_a_SOURCES = \
        ofproto/ofproto-dpif-governor.h \
        ofproto/ofproto-dpif-ipfix.c \
        ofproto/ofproto-dpif-ipfix.h \
+       ofproto/ofproto-dpif-mirror.c \
+       ofproto/ofproto-dpif-mirror.h \
        ofproto/ofproto-dpif-sflow.c \
        ofproto/ofproto-dpif-sflow.h \
        ofproto/ofproto-dpif-xlate.c \
index ef0e980..8523afb 100644 (file)
@@ -73,23 +73,25 @@ struct dpif_ipfix {
 #define IPFIX_TEMPLATE_INTERVAL 600
 
 /* Cf. IETF RFC 5101 Section 3.1. */
+OVS_PACKED(
 struct ipfix_header {
     ovs_be16 version;  /* IPFIX_VERSION. */
     ovs_be16 length;  /* Length in bytes including this header. */
     ovs_be32 export_time;  /* Seconds since the epoch. */
     ovs_be32 seq_number;  /* Message sequence number. */
     ovs_be32 obs_domain_id;  /* Observation Domain ID. */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_header) == 16);
 
 #define IPFIX_SET_ID_TEMPLATE 2
 #define IPFIX_SET_ID_OPTION_TEMPLATE 3
 
 /* Cf. IETF RFC 5101 Section 3.3.2. */
+OVS_PACKED(
 struct ipfix_set_header {
     ovs_be16 set_id;  /* IPFIX_SET_ID_* or valid template ID for Data Sets. */
     ovs_be16 length;  /* Length of the set in bytes including header. */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_set_header) == 4);
 
 /* Alternatives for templates at each layer.  A template is defined by
@@ -115,10 +117,11 @@ enum ipfix_proto_l4 {
 #define IPFIX_TEMPLATE_ID_MIN 256
 
 /* Cf. IETF RFC 5101 Section 3.4.1. */
+OVS_PACKED(
 struct ipfix_template_record_header {
     ovs_be16 template_id;
     ovs_be16 field_count;
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_template_record_header) == 4);
 
 enum ipfix_entity_id {
@@ -131,14 +134,16 @@ enum ipfix_entity_size {
 #include "ofproto/ipfix-entities.def"
 };
 
+OVS_PACKED(
 struct ipfix_template_field_specifier {
     ovs_be16 element_id;  /* IPFIX_ENTITY_ID_*. */
     ovs_be16 field_length;  /* Length of the field's value, in bytes. */
     /* No Enterprise ID, since only standard element IDs are specified. */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_template_field_specifier) == 4);
 
 /* Part of data record for common metadata and Ethernet entities. */
+OVS_PACKED(
 struct ipfix_data_record_common {
     ovs_be32 observation_point_id;  /* OBSERVATION_POINT_ID */
     ovs_be64 packet_delta_count;  /* PACKET_DELTA_COUNT */
@@ -148,18 +153,20 @@ struct ipfix_data_record_common {
     ovs_be16 ethernet_type;  /* ETHERNET_TYPE */
     ovs_be16 ethernet_total_length;  /* ETHERNET_TOTAL_LENGTH */
     uint8_t ethernet_header_length;  /* ETHERNET_HEADER_LENGTH */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_common) == 37);
 
 /* Part of data record for VLAN entities. */
+OVS_PACKED(
 struct ipfix_data_record_vlan {
     ovs_be16 vlan_id;  /* VLAN_ID */
     ovs_be16 dot1q_vlan_id;  /* DOT1Q_VLAN_ID */
     uint8_t dot1q_priority;  /* DOT1Q_PRIORITY */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_vlan) == 5);
 
 /* Part of data record for IP entities. */
+OVS_PACKED(
 struct ipfix_data_record_ip {
     uint8_t ip_version;  /* IP_VERSION */
     uint8_t ip_ttl;  /* IP_TTL */
@@ -167,29 +174,32 @@ struct ipfix_data_record_ip {
     uint8_t ip_diff_serv_code_point;  /* IP_DIFF_SERV_CODE_POINT */
     uint8_t ip_precedence;  /* IP_PRECEDENCE */
     uint8_t ip_class_of_service;  /* IP_CLASS_OF_SERVICE */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_ip) == 6);
 
 /* Part of data record for IPv4 entities. */
+OVS_PACKED(
 struct ipfix_data_record_ipv4 {
     ovs_be32 source_ipv4_address;  /* SOURCE_IPV4_ADDRESS */
     ovs_be32 destination_ipv4_address;  /* DESTINATION_IPV4_ADDRESS */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_ipv4) == 8);
 
 /* Part of data record for IPv4 entities. */
+OVS_PACKED(
 struct ipfix_data_record_ipv6 {
     uint8_t source_ipv6_address[16];  /* SOURCE_IPV6_ADDRESS */
     uint8_t destination_ipv6_address[16];  /* DESTINATION_IPV6_ADDRESS */
     ovs_be32 flow_label_ipv6;  /* FLOW_LABEL_IPV6 */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_ipv6) == 36);
 
 /* Part of data record for TCP/UDP entities. */
+OVS_PACKED(
 struct ipfix_data_record_tcpudp {
     ovs_be16 source_transport_port;  /* SOURCE_TRANSPORT_PORT */
     ovs_be16 destination_transport_port;  /* DESTINATION_TRANSPORT_PORT */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_tcpudp) == 4);
 
 static bool
diff --git a/ofproto/ofproto-dpif-mirror.c b/ofproto/ofproto-dpif-mirror.c
new file mode 100644 (file)
index 0000000..9734718
--- /dev/null
@@ -0,0 +1,500 @@
+/* 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.
+ * 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. */
+
+#include <config.h>
+
+#include "ofproto-dpif-mirror.h"
+
+#include <errno.h>
+
+#include "hmap.h"
+#include "hmapx.h"
+#include "ofproto.h"
+#include "vlan-bitmap.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(ofproto_dpif_mirror);
+
+#define MIRROR_MASK_C(X) UINT32_C(X)
+BUILD_ASSERT_DECL(sizeof(mirror_mask_t) * CHAR_BIT >= MAX_MIRRORS);
+
+struct mbridge {
+    struct mirror *mirrors[MAX_MIRRORS];
+    struct hmap mbundles;
+
+    bool need_revalidate;
+    bool has_mirrors;
+
+    int ref_cnt;
+};
+
+struct mbundle {
+    struct hmap_node hmap_node; /* In parent 'mbridge' map. */
+    struct ofbundle *ofbundle;
+
+    mirror_mask_t src_mirrors;  /* Mirrors triggered when packet received. */
+    mirror_mask_t dst_mirrors;  /* Mirrors triggered when packet sent. */
+    mirror_mask_t mirror_out;   /* Mirrors that output to this mbundle. */
+};
+
+struct mirror {
+    struct mbridge *mbridge;    /* Owning ofproto. */
+    size_t idx;                 /* In ofproto's "mirrors" array. */
+    void *aux;                  /* Key supplied by ofproto's client. */
+
+    /* Selection criteria. */
+    struct hmapx srcs;          /* Contains "struct mbundle*"s. */
+    struct hmapx dsts;          /* Contains "struct mbundle*"s. */
+    unsigned long *vlans;       /* Bitmap of chosen VLANs, NULL selects all. */
+
+    /* Output (exactly one of out == NULL and out_vlan == -1 is true). */
+    struct mbundle *out;        /* Output port or NULL. */
+    int out_vlan;               /* Output VLAN or -1. */
+    mirror_mask_t dup_mirrors;  /* Bitmap of mirrors with the same output. */
+
+    /* Counters. */
+    int64_t packet_count;       /* Number of packets sent. */
+    int64_t byte_count;         /* Number of bytes sent. */
+};
+
+static struct mirror *mirror_lookup(struct mbridge *, void *aux);
+static struct mbundle *mbundle_lookup(const struct mbridge *,
+                                      struct ofbundle *);
+static void mbundle_lookup_multiple(const struct mbridge *, struct ofbundle **,
+                                  size_t n_bundles, struct hmapx *mbundles);
+static int mirror_scan(struct mbridge *);
+static void mirror_update_dups(struct mbridge *);
+static int mirror_mask_ffs(mirror_mask_t);
+
+struct mbridge *
+mbridge_create(void)
+{
+    struct mbridge *mbridge;
+
+    mbridge = xzalloc(sizeof *mbridge);
+    mbridge->ref_cnt = 1;
+
+    hmap_init(&mbridge->mbundles);
+    return mbridge;
+}
+
+struct mbridge *
+mbridge_ref(const struct mbridge *mbridge_)
+{
+    struct mbridge *mbridge = CONST_CAST(struct mbridge *, mbridge_);
+    if (mbridge) {
+        ovs_assert(mbridge->ref_cnt > 0);
+        mbridge->ref_cnt++;
+    }
+    return mbridge;
+}
+
+void
+mbridge_unref(struct mbridge *mbridge)
+{
+    struct mbundle *mbundle, *next;
+    size_t i;
+
+    if (!mbridge) {
+        return;
+    }
+
+    ovs_assert(mbridge->ref_cnt > 0);
+    if (--mbridge->ref_cnt) {
+        return;
+    }
+
+    for (i = 0; i < MAX_MIRRORS; i++) {
+        if (mbridge->mirrors[i]) {
+            mirror_destroy(mbridge, mbridge->mirrors[i]->aux);
+        }
+    }
+
+    HMAP_FOR_EACH_SAFE (mbundle, next, hmap_node, &mbridge->mbundles) {
+        mbridge_unregister_bundle(mbridge, mbundle->ofbundle);
+    }
+
+    free(mbridge);
+}
+
+bool
+mbridge_has_mirrors(struct mbridge *mbridge)
+{
+    return mbridge ? mbridge->has_mirrors : false;
+}
+
+/* Returns true if configurations changes in 'mbridge''s mirrors require
+ * revalidation. */
+bool
+mbridge_need_revalidate(struct mbridge *mbridge)
+{
+    return mbridge->need_revalidate;
+}
+
+void
+mbridge_register_bundle(struct mbridge *mbridge, struct ofbundle *ofbundle)
+{
+    struct mbundle *mbundle;
+
+    mbundle = xzalloc(sizeof *mbundle);
+    mbundle->ofbundle = ofbundle;
+    hmap_insert(&mbridge->mbundles, &mbundle->hmap_node,
+                hash_pointer(ofbundle, 0));
+}
+
+void
+mbridge_unregister_bundle(struct mbridge *mbridge, struct ofbundle *ofbundle)
+{
+    struct mbundle *mbundle = mbundle_lookup(mbridge, ofbundle);
+    size_t i;
+
+    if (!mbundle) {
+        return;
+    }
+
+    for (i = 0; i < MAX_MIRRORS; i++) {
+        struct mirror *m = mbridge->mirrors[i];
+        if (m) {
+            if (m->out == mbundle) {
+                mirror_destroy(mbridge, m->aux);
+            } else if (hmapx_find_and_delete(&m->srcs, mbundle)
+                       || hmapx_find_and_delete(&m->dsts, mbundle)) {
+                mbridge->need_revalidate = true;
+            }
+        }
+    }
+
+    hmap_remove(&mbridge->mbundles, &mbundle->hmap_node);
+    free(mbundle);
+}
+
+mirror_mask_t
+mirror_bundle_out(struct mbridge *mbridge, struct ofbundle *ofbundle)
+{
+    struct mbundle *mbundle = mbundle_lookup(mbridge, ofbundle);
+    return mbundle ? mbundle->mirror_out : 0;
+}
+
+mirror_mask_t
+mirror_bundle_src(struct mbridge *mbridge, struct ofbundle *ofbundle)
+{
+    struct mbundle *mbundle = mbundle_lookup(mbridge, ofbundle);
+    return mbundle ? mbundle->src_mirrors : 0;
+}
+
+mirror_mask_t
+mirror_bundle_dst(struct mbridge *mbridge, struct ofbundle *ofbundle)
+{
+    struct mbundle *mbundle = mbundle_lookup(mbridge, ofbundle);
+    return mbundle ? mbundle->dst_mirrors : 0;
+}
+
+int
+mirror_set(struct mbridge *mbridge, void *aux, const char *name,
+           struct ofbundle **srcs, size_t n_srcs,
+           struct ofbundle **dsts, size_t n_dsts,
+           unsigned long *src_vlans, struct ofbundle *out_bundle,
+           uint16_t out_vlan)
+{
+    struct mbundle *mbundle, *out;
+    mirror_mask_t mirror_bit;
+    struct mirror *mirror;
+    struct hmapx srcs_map;          /* Contains "struct ofbundle *"s. */
+    struct hmapx dsts_map;          /* Contains "struct ofbundle *"s. */
+
+    mirror = mirror_lookup(mbridge, aux);
+    if (!mirror) {
+        int idx;
+
+        idx = mirror_scan(mbridge);
+        if (idx < 0) {
+            VLOG_WARN("maximum of %d port mirrors reached, cannot create %s",
+                      MAX_MIRRORS, name);
+            return EFBIG;
+        }
+
+        mirror = mbridge->mirrors[idx] = xzalloc(sizeof *mirror);
+        mirror->mbridge = mbridge;
+        mirror->idx = idx;
+        mirror->aux = aux;
+        mirror->out_vlan = -1;
+    }
+
+    /* Get the new configuration. */
+    if (out_bundle) {
+        out = mbundle_lookup(mbridge, out_bundle);
+        if (!out) {
+            mirror_destroy(mbridge, mirror->aux);
+            return EINVAL;
+        }
+        out_vlan = -1;
+    } else {
+        out = NULL;
+    }
+    mbundle_lookup_multiple(mbridge, srcs, n_srcs, &srcs_map);
+    mbundle_lookup_multiple(mbridge, dsts, n_dsts, &dsts_map);
+
+    /* If the configuration has not changed, do nothing. */
+    if (hmapx_equals(&srcs_map, &mirror->srcs)
+        && hmapx_equals(&dsts_map, &mirror->dsts)
+        && vlan_bitmap_equal(mirror->vlans, src_vlans)
+        && mirror->out == out
+        && mirror->out_vlan == out_vlan)
+    {
+        hmapx_destroy(&srcs_map);
+        hmapx_destroy(&dsts_map);
+        return 0;
+    }
+
+    hmapx_swap(&srcs_map, &mirror->srcs);
+    hmapx_destroy(&srcs_map);
+
+    hmapx_swap(&dsts_map, &mirror->dsts);
+    hmapx_destroy(&dsts_map);
+
+    free(mirror->vlans);
+    mirror->vlans = vlan_bitmap_clone(src_vlans);
+
+    mirror->out = out;
+    mirror->out_vlan = out_vlan;
+
+    /* Update mbundles. */
+    mirror_bit = MIRROR_MASK_C(1) << mirror->idx;
+    HMAP_FOR_EACH (mbundle, hmap_node, &mirror->mbridge->mbundles) {
+        if (hmapx_contains(&mirror->srcs, mbundle)) {
+            mbundle->src_mirrors |= mirror_bit;
+        } else {
+            mbundle->src_mirrors &= ~mirror_bit;
+        }
+
+        if (hmapx_contains(&mirror->dsts, mbundle)) {
+            mbundle->dst_mirrors |= mirror_bit;
+        } else {
+            mbundle->dst_mirrors &= ~mirror_bit;
+        }
+
+        if (mirror->out == mbundle) {
+            mbundle->mirror_out |= mirror_bit;
+        } else {
+            mbundle->mirror_out &= ~mirror_bit;
+        }
+    }
+
+    mbridge->has_mirrors = true;
+    mirror_update_dups(mbridge);
+
+    return 0;
+}
+
+void
+mirror_destroy(struct mbridge *mbridge, void *aux)
+{
+    struct mirror *mirror = mirror_lookup(mbridge, aux);
+    mirror_mask_t mirror_bit;
+    struct mbundle *mbundle;
+    int i;
+
+    if (!mirror) {
+        return;
+    }
+
+    mirror_bit = MIRROR_MASK_C(1) << mirror->idx;
+    HMAP_FOR_EACH (mbundle, hmap_node, &mbridge->mbundles) {
+        mbundle->src_mirrors &= ~mirror_bit;
+        mbundle->dst_mirrors &= ~mirror_bit;
+        mbundle->mirror_out &= ~mirror_bit;
+    }
+
+    hmapx_destroy(&mirror->srcs);
+    hmapx_destroy(&mirror->dsts);
+    free(mirror->vlans);
+
+    mbridge->mirrors[mirror->idx] = NULL;
+    free(mirror);
+
+    mirror_update_dups(mbridge);
+
+    mbridge->has_mirrors = false;
+    for (i = 0; i < MAX_MIRRORS; i++) {
+        if (mbridge->mirrors[i]) {
+            mbridge->has_mirrors = true;
+            break;
+        }
+    }
+}
+
+int
+mirror_get_stats(struct mbridge *mbridge, void *aux, uint64_t *packets,
+                 uint64_t *bytes)
+{
+    struct mirror *mirror = mirror_lookup(mbridge, aux);
+
+    if (!mirror) {
+        *packets = *bytes = UINT64_MAX;
+        return 0;
+    }
+
+    *packets = mirror->packet_count;
+    *bytes = mirror->byte_count;
+
+    return 0;
+}
+
+void
+mirror_update_stats(struct mbridge *mbridge, mirror_mask_t mirrors,
+                    uint64_t packets, uint64_t bytes)
+{
+    if (!mbridge || !mirrors) {
+        return;
+    }
+
+    for (; mirrors; mirrors = zero_rightmost_1bit(mirrors)) {
+        struct mirror *m;
+
+        m = mbridge->mirrors[mirror_mask_ffs(mirrors) - 1];
+
+        if (!m) {
+            /* In normal circumstances 'm' will not be NULL.  However,
+             * if mirrors are reconfigured, we can temporarily get out
+             * of sync in facet_revalidate().  We could "correct" the
+             * mirror list before reaching here, but doing that would
+             * not properly account the traffic stats we've currently
+             * accumulated for previous mirror configuration. */
+            continue;
+        }
+
+        m->packet_count += packets;
+        m->byte_count += bytes;
+    }
+}
+
+/* Retrieves the mirror in 'mbridge' represented by the first bet set of
+ * 'mirrors'.  Returns true if such a mirror exists, false otherwise.
+ * The caller takes ownership of, and is expected to deallocate, 'vlans' */
+bool
+mirror_get(struct mbridge *mbridge, int index, unsigned long **vlans,
+           mirror_mask_t *dup_mirrors, struct ofbundle **out, int *out_vlan)
+{
+    struct mirror *mirror;
+
+    if (!mbridge) {
+        return false;
+    }
+
+    mirror = mbridge->mirrors[index];
+    if (!mirror) {
+        return false;
+    }
+
+    *vlans = vlan_bitmap_clone(mirror->vlans);
+    *dup_mirrors = mirror->dup_mirrors;
+    *out = mirror->out ? mirror->out->ofbundle : NULL;
+    *out_vlan = mirror->out_vlan;
+    return true;
+}
+\f
+/* Helpers. */
+
+static struct mbundle *
+mbundle_lookup(const struct mbridge *mbridge, struct ofbundle *ofbundle)
+{
+    struct mbundle *mbundle;
+
+    HMAP_FOR_EACH_IN_BUCKET (mbundle, hmap_node, hash_pointer(ofbundle, 0),
+                             &mbridge->mbundles) {
+        if (mbundle->ofbundle == ofbundle) {
+            return mbundle;
+        }
+    }
+    return NULL;
+}
+
+/* Looks up each of the 'n_ofbundlees' pointers in 'ofbundlees' as mbundles and
+ * adds the ones that are found to 'mbundles'. */
+static void
+mbundle_lookup_multiple(const struct mbridge *mbridge,
+                        struct ofbundle **ofbundles, size_t n_ofbundles,
+                        struct hmapx *mbundles)
+{
+    size_t i;
+
+    hmapx_init(mbundles);
+    for (i = 0; i < n_ofbundles; i++) {
+        struct mbundle *mbundle = mbundle_lookup(mbridge, ofbundles[i]);
+        if (mbundle) {
+            hmapx_add(mbundles, mbundle);
+        }
+    }
+}
+
+static int
+mirror_scan(struct mbridge *mbridge)
+{
+    int idx;
+
+    for (idx = 0; idx < MAX_MIRRORS; idx++) {
+        if (!mbridge->mirrors[idx]) {
+            return idx;
+        }
+    }
+    return -1;
+}
+
+static struct mirror *
+mirror_lookup(struct mbridge *mbridge, void *aux)
+{
+    int i;
+
+    for (i = 0; i < MAX_MIRRORS; i++) {
+        struct mirror *mirror = mbridge->mirrors[i];
+        if (mirror && mirror->aux == aux) {
+            return mirror;
+        }
+    }
+
+    return NULL;
+}
+
+/* Update the 'dup_mirrors' member of each of the mirrors in 'ofproto'. */
+static void
+mirror_update_dups(struct mbridge *mbridge)
+{
+    int i;
+
+    for (i = 0; i < MAX_MIRRORS; i++) {
+        struct mirror *m = mbridge->mirrors[i];
+
+        if (m) {
+            m->dup_mirrors = MIRROR_MASK_C(1) << i;
+        }
+    }
+
+    for (i = 0; i < MAX_MIRRORS; i++) {
+        struct mirror *m1 = mbridge->mirrors[i];
+        int j;
+
+        if (!m1) {
+            continue;
+        }
+
+        for (j = i + 1; j < MAX_MIRRORS; j++) {
+            struct mirror *m2 = mbridge->mirrors[j];
+
+            if (m2 && m1->out == m2->out && m1->out_vlan == m2->out_vlan) {
+                m1->dup_mirrors |= MIRROR_MASK_C(1) << j;
+                m2->dup_mirrors |= m1->dup_mirrors;
+            }
+        }
+    }
+}
diff --git a/ofproto/ofproto-dpif-mirror.h b/ofproto/ofproto-dpif-mirror.h
new file mode 100644 (file)
index 0000000..4a6f3ce
--- /dev/null
@@ -0,0 +1,61 @@
+/* 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. */
+
+#ifndef OFPROT_DPIF_MIRROR_H
+#define OFPROT_DPIF_MIRROR_H 1
+
+#include <stdint.h>
+
+#include "util.h"
+
+#define MAX_MIRRORS 32
+typedef uint32_t mirror_mask_t;
+
+struct ofproto_dpif;
+struct ofbundle;
+
+struct mbridge *mbridge_create(void);
+struct mbridge *mbridge_ref(const struct mbridge *);
+void mbridge_unref(struct mbridge *);
+bool mbridge_has_mirrors(struct mbridge *);
+bool mbridge_need_revalidate(struct mbridge *);
+
+void mbridge_register_bundle(struct mbridge *, struct ofbundle *);
+void mbridge_unregister_bundle(struct mbridge *, struct ofbundle *);
+
+mirror_mask_t mirror_bundle_out(struct mbridge *, struct ofbundle *);
+mirror_mask_t mirror_bundle_src(struct mbridge *, struct ofbundle *);
+mirror_mask_t mirror_bundle_dst(struct mbridge *, struct ofbundle *);
+
+int mirror_set(struct mbridge *, void *aux, const char *name,
+               struct ofbundle **srcs, size_t n_srcs,
+               struct ofbundle **dsts, size_t n_dsts,
+               unsigned long *src_vlans, struct ofbundle *out_bundle,
+               uint16_t out_vlan);
+void mirror_destroy(struct mbridge *, void *aux);
+int mirror_get_stats(struct mbridge *, void *aux, uint64_t *packets,
+                     uint64_t *bytes);
+void mirror_update_stats(struct mbridge*, mirror_mask_t, uint64_t packets,
+                         uint64_t bytes);
+bool mirror_get(struct mbridge *, int index, unsigned long **vlans,
+                mirror_mask_t *dup_mirrors, struct ofbundle **out,
+                int *out_vlan);
+
+static inline int
+mirror_mask_ffs(mirror_mask_t mask)
+{
+    BUILD_ASSERT_DECL(sizeof(unsigned int) >= sizeof(mask));
+    return ffs(mask);
+}
+#endif /* ofproto-dpif-mirror.h */
index 0e7b9a0..bd96910 100644 (file)
@@ -29,6 +29,7 @@
 #include "in-band.h"
 #include "lacp.h"
 #include "learn.h"
+#include "list.h"
 #include "mac-learning.h"
 #include "meta-flow.h"
 #include "multipath.h"
 #include "odp-execute.h"
 #include "ofp-actions.h"
 #include "ofproto/ofproto-dpif-ipfix.h"
+#include "ofproto/ofproto-dpif-mirror.h"
 #include "ofproto/ofproto-dpif-sflow.h"
 #include "ofproto/ofproto-dpif.h"
 #include "tunnel.h"
 #include "vlog.h"
 
-COVERAGE_DEFINE(ofproto_dpif_xlate);
+COVERAGE_DEFINE(xlate_actions);
 
 VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
 
@@ -51,11 +53,79 @@ VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
  * flow translation. */
 #define MAX_RESUBMIT_RECURSION 64
 
+struct xbridge {
+    struct hmap_node hmap_node;   /* Node in global 'xbridges' map. */
+    struct ofproto_dpif *ofproto; /* Key in global 'xbridges' map. */
+
+    struct list xbundles;         /* Owned xbundles. */
+    struct hmap xports;           /* Indexed by ofp_port. */
+
+    char *name;                   /* Name used in log messages. */
+    struct mac_learning *ml;      /* Mac learning handle. */
+    struct mbridge *mbridge;      /* Mirroring. */
+    struct dpif_sflow *sflow;     /* SFlow handle, or null. */
+    struct dpif_ipfix *ipfix;     /* Ipfix handle, or null. */
+
+    enum ofp_config_flags frag;   /* Fragmentation handling. */
+    bool has_stp;                 /* Bridge runs stp? */
+    bool has_netflow;             /* Bridge runs netflow? */
+    bool has_in_band;             /* Bridge has in band control? */
+    bool forward_bpdu;            /* Bridge forwards STP BPDUs? */
+};
+
+struct xbundle {
+    struct hmap_node hmap_node;    /* In global 'xbundles' map. */
+    struct ofbundle *ofbundle;     /* Key in global 'xbundles' map. */
+
+    struct list list_node;         /* In parent 'xbridges' list. */
+    struct xbridge *xbridge;       /* Parent xbridge. */
+
+    struct list xports;            /* Contains "struct xport"s. */
+
+    char *name;                    /* Name used in log messages. */
+    struct bond *bond;             /* Nonnull iff more than one port. */
+    struct lacp *lacp;             /* LACP handle or null. */
+
+    enum port_vlan_mode vlan_mode; /* VLAN mode. */
+    int vlan;                      /* -1=trunk port, else a 12-bit VLAN ID. */
+    unsigned long *trunks;         /* Bitmap of trunked VLANs, if 'vlan' == -1.
+                                    * NULL if all VLANs are trunked. */
+    bool use_priority_tags;        /* Use 802.1p tag for frames in VLAN 0? */
+    bool floodable;                /* No port has OFPUTIL_PC_NO_FLOOD set? */
+};
+
+struct xport {
+    struct hmap_node hmap_node;      /* Node in global 'xports' map. */
+    struct ofport_dpif *ofport;      /* Key in global 'xports map. */
+
+    struct hmap_node ofp_node;       /* Node in parent xbridge 'xports' map. */
+    ofp_port_t ofp_port;             /* Key in parent xbridge 'xports' map. */
+
+    odp_port_t odp_port;             /* Datapath port number or ODPP_NONE. */
+
+    struct list bundle_node;         /* In parent xbundle (if it exists). */
+    struct xbundle *xbundle;         /* Parent xbundle or null. */
+
+    struct netdev *netdev;           /* 'ofport''s netdev. */
+
+    struct xbridge *xbridge;         /* Parent bridge. */
+    struct xport *peer;              /* Patch port peer or null. */
+
+    enum ofputil_port_config config; /* OpenFlow port configuration. */
+    enum stp_state stp_state;        /* STP_DISABLED if STP not in use. */
+
+    bool may_enable;                 /* May be enabled in bonds. */
+    bool is_tunnel;                  /* Is a tunnel port. */
+
+    struct cfm *cfm;                 /* CFM handle or null. */
+    struct bfd *bfd;                 /* BFD handle or null. */
+};
+
 struct xlate_ctx {
     struct xlate_in *xin;
     struct xlate_out *xout;
 
-    struct ofproto_dpif *ofproto;
+    const struct xbridge *xbridge;
 
     /* Flow at the last commit. */
     struct flow base_flow;
@@ -91,58 +161,370 @@ struct xlate_ctx {
  * when an input bundle is needed for validation (e.g., mirroring or
  * OFPP_NORMAL processing).  It is not connected to an 'ofproto' or have
  * any 'port' structs, so care must be taken when dealing with it. */
-static struct ofbundle ofpp_none_bundle = {
+static struct xbundle ofpp_none_bundle = {
     .name      = "OFPP_NONE",
     .vlan_mode = PORT_VLAN_TRUNK
 };
 
-static bool may_receive(const struct ofport_dpif *, struct xlate_ctx *);
+static struct hmap xbridges = HMAP_INITIALIZER(&xbridges);
+static struct hmap xbundles = HMAP_INITIALIZER(&xbundles);
+static struct hmap xports = HMAP_INITIALIZER(&xports);
+
+static bool may_receive(const struct xport *, struct xlate_ctx *);
 static void do_xlate_actions(const struct ofpact *, size_t ofpacts_len,
                              struct xlate_ctx *);
 static void xlate_normal(struct xlate_ctx *);
 static void xlate_report(struct xlate_ctx *, const char *);
 static void xlate_table_action(struct xlate_ctx *, ofp_port_t in_port,
                                uint8_t table_id, bool may_packet_in);
-static bool input_vid_is_valid(uint16_t vid, struct ofbundle *, bool warn);
-static uint16_t input_vid_to_vlan(const struct ofbundle *, uint16_t vid);
-static void output_normal(struct xlate_ctx *, const struct ofbundle *,
+static bool input_vid_is_valid(uint16_t vid, struct xbundle *, bool warn);
+static uint16_t input_vid_to_vlan(const struct xbundle *, uint16_t vid);
+static void output_normal(struct xlate_ctx *, const struct xbundle *,
                           uint16_t vlan);
 static void compose_output_action(struct xlate_ctx *, ofp_port_t ofp_port);
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
+static struct xbridge *xbridge_lookup(const struct ofproto_dpif *);
+static struct xbundle *xbundle_lookup(const struct ofbundle *);
+static struct xport *xport_lookup(struct ofport_dpif *);
+static struct xport *get_ofp_port(const struct xbridge *, ofp_port_t ofp_port);
+
+void
+xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
+                  const struct mac_learning *ml, const struct mbridge *mbridge,
+                  const struct dpif_sflow *sflow,
+                  const struct dpif_ipfix *ipfix, enum ofp_config_flags frag,
+                  bool forward_bpdu, bool has_in_band, bool has_netflow,
+                  bool has_stp)
+{
+    struct xbridge *xbridge = xbridge_lookup(ofproto);
+
+    if (!xbridge) {
+        xbridge = xzalloc(sizeof *xbridge);
+        xbridge->ofproto = ofproto;
+
+        hmap_insert(&xbridges, &xbridge->hmap_node, hash_pointer(ofproto, 0));
+        hmap_init(&xbridge->xports);
+        list_init(&xbridge->xbundles);
+    }
+
+    if (xbridge->ml != ml) {
+        mac_learning_unref(xbridge->ml);
+        xbridge->ml = mac_learning_ref(ml);
+    }
+
+    if (xbridge->mbridge != mbridge) {
+        mbridge_unref(xbridge->mbridge);
+        xbridge->mbridge = mbridge_ref(mbridge);
+    }
+
+    if (xbridge->sflow != sflow) {
+        dpif_sflow_unref(xbridge->sflow);
+        xbridge->sflow = dpif_sflow_ref(sflow);
+    }
+
+    if (xbridge->ipfix != ipfix) {
+        dpif_ipfix_unref(xbridge->ipfix);
+        xbridge->ipfix = dpif_ipfix_ref(ipfix);
+    }
+
+    free(xbridge->name);
+    xbridge->name = xstrdup(name);
+
+    xbridge->forward_bpdu = forward_bpdu;
+    xbridge->has_in_band = has_in_band;
+    xbridge->has_netflow = has_netflow;
+    xbridge->has_stp = has_stp;
+    xbridge->frag = frag;
+}
+
+void
+xlate_remove_ofproto(struct ofproto_dpif *ofproto)
+{
+    struct xbridge *xbridge = xbridge_lookup(ofproto);
+    struct xbundle *xbundle, *next_xbundle;
+    struct xport *xport, *next_xport;
+
+    if (!xbridge) {
+        return;
+    }
+
+    HMAP_FOR_EACH_SAFE (xport, next_xport, ofp_node, &xbridge->xports) {
+        xlate_ofport_remove(xport->ofport);
+    }
+
+    LIST_FOR_EACH_SAFE (xbundle, next_xbundle, list_node, &xbridge->xbundles) {
+        xlate_bundle_remove(xbundle->ofbundle);
+    }
+
+    hmap_remove(&xbridges, &xbridge->hmap_node);
+    free(xbridge->name);
+    free(xbridge);
+}
+
+void
+xlate_bundle_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
+                 const char *name, enum port_vlan_mode vlan_mode, int vlan,
+                 unsigned long *trunks, bool use_priority_tags,
+                 const struct bond *bond, const struct lacp *lacp,
+                 bool floodable)
+{
+    struct xbundle *xbundle = xbundle_lookup(ofbundle);
+
+    if (!xbundle) {
+        xbundle = xzalloc(sizeof *xbundle);
+        xbundle->ofbundle = ofbundle;
+        xbundle->xbridge = xbridge_lookup(ofproto);
+
+        hmap_insert(&xbundles, &xbundle->hmap_node, hash_pointer(ofbundle, 0));
+        list_insert(&xbundle->xbridge->xbundles, &xbundle->list_node);
+        list_init(&xbundle->xports);
+    }
+
+    ovs_assert(xbundle->xbridge);
+
+    free(xbundle->name);
+    xbundle->name = xstrdup(name);
+
+    xbundle->vlan_mode = vlan_mode;
+    xbundle->vlan = vlan;
+    xbundle->trunks = trunks;
+    xbundle->use_priority_tags = use_priority_tags;
+    xbundle->floodable = floodable;
+
+    if (xbundle->bond != bond) {
+        bond_unref(xbundle->bond);
+        xbundle->bond = bond_ref(bond);
+    }
+
+    if (xbundle->lacp != lacp) {
+        lacp_unref(xbundle->lacp);
+        xbundle->lacp = lacp_ref(lacp);
+    }
+}
+
+void
+xlate_bundle_remove(struct ofbundle *ofbundle)
+{
+    struct xbundle *xbundle = xbundle_lookup(ofbundle);
+    struct xport *xport, *next;
+
+    if (!xbundle) {
+        return;
+    }
+
+    LIST_FOR_EACH_SAFE (xport, next, bundle_node, &xbundle->xports) {
+        list_remove(&xport->bundle_node);
+        xport->xbundle = NULL;
+    }
+
+    hmap_remove(&xbundles, &xbundle->hmap_node);
+    list_remove(&xbundle->list_node);
+    bond_unref(xbundle->bond);
+    lacp_unref(xbundle->lacp);
+    free(xbundle->name);
+    free(xbundle);
+}
+
+void
+xlate_ofport_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
+                 struct ofport_dpif *ofport, ofp_port_t ofp_port,
+                 odp_port_t odp_port, const struct netdev *netdev,
+                 const struct cfm *cfm, const struct bfd *bfd,
+                 struct ofport_dpif *peer, enum ofputil_port_config config,
+                 enum stp_state stp_state, bool is_tunnel, bool may_enable)
+{
+    struct xport *xport = xport_lookup(ofport);
+
+    if (!xport) {
+        xport = xzalloc(sizeof *xport);
+        xport->ofport = ofport;
+        xport->xbridge = xbridge_lookup(ofproto);
+        xport->ofp_port = ofp_port;
+
+        hmap_insert(&xports, &xport->hmap_node, hash_pointer(ofport, 0));
+        hmap_insert(&xport->xbridge->xports, &xport->ofp_node,
+                    hash_ofp_port(xport->ofp_port));
+    }
+
+    ovs_assert(xport->ofp_port == ofp_port);
+
+    xport->config = config;
+    xport->stp_state = stp_state;
+    xport->is_tunnel = is_tunnel;
+    xport->may_enable = may_enable;
+    xport->odp_port = odp_port;
+
+    if (xport->netdev != netdev) {
+        netdev_close(xport->netdev);
+        xport->netdev = netdev_ref(netdev);
+    }
+
+    if (xport->cfm != cfm) {
+        cfm_unref(xport->cfm);
+        xport->cfm = cfm_ref(cfm);
+    }
+
+    if (xport->bfd != bfd) {
+        bfd_unref(xport->bfd);
+        xport->bfd = bfd_ref(bfd);
+    }
+
+    if (xport->peer) {
+        xport->peer->peer = NULL;
+    }
+    xport->peer = peer ? xport_lookup(peer) : NULL;
+    if (xport->peer) {
+        xport->peer->peer = xport;
+    }
+
+    if (xport->xbundle) {
+        list_remove(&xport->bundle_node);
+    }
+    xport->xbundle = ofbundle ? xbundle_lookup(ofbundle) : NULL;
+    if (xport->xbundle) {
+        list_insert(&xport->xbundle->xports, &xport->bundle_node);
+    }
+}
+
+void
+xlate_ofport_remove(struct ofport_dpif *ofport)
+{
+    struct xport *xport = xport_lookup(ofport);
+
+    if (!xport) {
+        return;
+    }
+
+    if (xport->peer) {
+        xport->peer->peer = NULL;
+        xport->peer = NULL;
+    }
+
+    list_remove(&xport->bundle_node);
+    hmap_remove(&xports, &xport->hmap_node);
+    hmap_remove(&xport->xbridge->xports, &xport->ofp_node);
+
+    netdev_close(xport->netdev);
+    cfm_unref(xport->cfm);
+    bfd_unref(xport->bfd);
+    free(xport);
+}
+
+static struct xbridge *
+xbridge_lookup(const struct ofproto_dpif *ofproto)
+{
+    struct xbridge *xbridge;
+
+    HMAP_FOR_EACH_IN_BUCKET (xbridge, hmap_node, hash_pointer(ofproto, 0),
+                             &xbridges) {
+        if (xbridge->ofproto == ofproto) {
+            return xbridge;
+        }
+    }
+    return NULL;
+}
+
+static struct xbundle *
+xbundle_lookup(const struct ofbundle *ofbundle)
+{
+    struct xbundle *xbundle;
+
+    HMAP_FOR_EACH_IN_BUCKET (xbundle, hmap_node, hash_pointer(ofbundle, 0),
+                             &xbundles) {
+        if (xbundle->ofbundle == ofbundle) {
+            return xbundle;
+        }
+    }
+    return NULL;
+}
+
+static struct xport *
+xport_lookup(struct ofport_dpif *ofport)
+{
+    struct xport *xport;
+
+    HMAP_FOR_EACH_IN_BUCKET (xport, hmap_node, hash_pointer(ofport, 0),
+                             &xports) {
+        if (xport->ofport == ofport) {
+            return xport;
+        }
+    }
+    return NULL;
+}
+
+static struct xport *
+get_ofp_port(const struct xbridge *xbridge, ofp_port_t ofp_port)
+{
+    struct xport *xport;
+
+    HMAP_FOR_EACH_IN_BUCKET (xport, ofp_node, hash_ofp_port(ofp_port),
+                             &xbridge->xports) {
+        if (xport->ofp_port == ofp_port) {
+            return xport;
+        }
+    }
+    return NULL;
+}
+
+static odp_port_t
+ofp_port_to_odp_port(const struct xbridge *xbridge, ofp_port_t ofp_port)
+{
+    const struct xport *xport = get_ofp_port(xbridge, ofp_port);
+    return xport ? xport->odp_port : ODPP_NONE;
+}
+
 static bool
-ofbundle_trunks_vlan(const struct ofbundle *bundle, uint16_t vlan)
+xbundle_trunks_vlan(const struct xbundle *bundle, uint16_t vlan)
 {
     return (bundle->vlan_mode != PORT_VLAN_ACCESS
             && (!bundle->trunks || bitmap_is_set(bundle->trunks, vlan)));
 }
 
 static bool
-ofbundle_includes_vlan(const struct ofbundle *bundle, uint16_t vlan)
+xbundle_includes_vlan(const struct xbundle *xbundle, uint16_t vlan)
 {
-    return vlan == bundle->vlan || ofbundle_trunks_vlan(bundle, vlan);
+    return vlan == xbundle->vlan || xbundle_trunks_vlan(xbundle, vlan);
 }
 
-static bool
-vlan_is_mirrored(const struct ofmirror *m, int vlan)
+static mirror_mask_t
+xbundle_mirror_out(const struct xbridge *xbridge, struct xbundle *xbundle)
+{
+    return xbundle != &ofpp_none_bundle
+        ? mirror_bundle_out(xbridge->mbridge, xbundle->ofbundle)
+        : 0;
+}
+
+static mirror_mask_t
+xbundle_mirror_src(const struct xbridge *xbridge, struct xbundle *xbundle)
+{
+    return xbundle != &ofpp_none_bundle
+        ? mirror_bundle_src(xbridge->mbridge, xbundle->ofbundle)
+        : 0;
+}
+
+static mirror_mask_t
+xbundle_mirror_dst(const struct xbridge *xbridge, struct xbundle *xbundle)
 {
-    return !m->vlans || bitmap_is_set(m->vlans, vlan);
+    return xbundle != &ofpp_none_bundle
+        ? mirror_bundle_dst(xbridge->mbridge, xbundle->ofbundle)
+        : 0;
 }
 
-static struct ofbundle *
-lookup_input_bundle(const struct ofproto_dpif *ofproto, ofp_port_t in_port,
-                    bool warn, struct ofport_dpif **in_ofportp)
+static struct xbundle *
+lookup_input_bundle(const struct xbridge *xbridge, ofp_port_t in_port,
+                    bool warn, struct xport **in_xportp)
 {
-    struct ofport_dpif *ofport;
+    struct xport *xport;
 
     /* Find the port and bundle for the received packet. */
-    ofport = get_ofp_port(ofproto, in_port);
-    if (in_ofportp) {
-        *in_ofportp = ofport;
+    xport = get_ofp_port(xbridge, in_port);
+    if (in_xportp) {
+        *in_xportp = xport;
     }
-    if (ofport && ofport->bundle) {
-        return ofport->bundle;
+    if (xport && xport->xbundle) {
+        return xport->xbundle;
     }
 
     /* Special-case OFPP_NONE, which a controller may use as the ingress
@@ -168,7 +550,7 @@ lookup_input_bundle(const struct ofproto_dpif *ofproto, ofp_port_t in_port,
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
         VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown "
-                     "port %"PRIu16, ofproto->up.name, in_port);
+                     "port %"PRIu16, xbridge->name, in_port);
     }
     return NULL;
 }
@@ -176,55 +558,40 @@ lookup_input_bundle(const struct ofproto_dpif *ofproto, ofp_port_t in_port,
 static void
 add_mirror_actions(struct xlate_ctx *ctx, const struct flow *orig_flow)
 {
-    struct ofproto_dpif *ofproto = ctx->ofproto;
+    const struct xbridge *xbridge = ctx->xbridge;
     mirror_mask_t mirrors;
-    struct ofbundle *in_bundle;
+    struct xbundle *in_xbundle;
     uint16_t vlan;
     uint16_t vid;
-    const struct nlattr *a;
-    size_t left;
 
-    in_bundle = lookup_input_bundle(ctx->ofproto, orig_flow->in_port.ofp_port,
-                                    ctx->xin->packet != NULL, NULL);
-    if (!in_bundle) {
+    mirrors = ctx->xout->mirrors;
+    ctx->xout->mirrors = 0;
+
+    in_xbundle = lookup_input_bundle(xbridge, orig_flow->in_port.ofp_port,
+                                     ctx->xin->packet != NULL, NULL);
+    if (!in_xbundle) {
         return;
     }
-    mirrors = in_bundle->src_mirrors;
+    mirrors |= xbundle_mirror_src(xbridge, in_xbundle);
 
     /* Drop frames on bundles reserved for mirroring. */
-    if (in_bundle->mirror_out) {
+    if (xbundle_mirror_out(xbridge, in_xbundle)) {
         if (ctx->xin->packet != NULL) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
             VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port "
                          "%s, which is reserved exclusively for mirroring",
-                         ctx->ofproto->up.name, in_bundle->name);
+                         ctx->xbridge->name, in_xbundle->name);
         }
+        ofpbuf_clear(&ctx->xout->odp_actions);
         return;
     }
 
     /* Check VLAN. */
     vid = vlan_tci_to_vid(orig_flow->vlan_tci);
-    if (!input_vid_is_valid(vid, in_bundle, ctx->xin->packet != NULL)) {
+    if (!input_vid_is_valid(vid, in_xbundle, ctx->xin->packet != NULL)) {
         return;
     }
-    vlan = input_vid_to_vlan(in_bundle, vid);
-
-    /* Look at the output ports to check for destination selections. */
-
-    NL_ATTR_FOR_EACH (a, left, ctx->xout->odp_actions.data,
-                      ctx->xout->odp_actions.size) {
-        enum ovs_action_attr type = nl_attr_type(a);
-        struct ofport_dpif *ofport;
-
-        if (type != OVS_ACTION_ATTR_OUTPUT) {
-            continue;
-        }
-
-        ofport = get_odp_port(ofproto, nl_attr_get_odp_port(a));
-        if (ofport && ofport->bundle) {
-            mirrors |= ofport->bundle->dst_mirrors;
-        }
-    }
+    vlan = input_vid_to_vlan(in_xbundle, vid);
 
     if (!mirrors) {
         return;
@@ -234,31 +601,43 @@ add_mirror_actions(struct xlate_ctx *ctx, const struct flow *orig_flow)
     ctx->xin->flow = *orig_flow;
 
     while (mirrors) {
-        struct ofmirror *m;
-
-        m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1];
-
-        if (m->vlans) {
+        mirror_mask_t dup_mirrors;
+        struct ofbundle *out;
+        unsigned long *vlans;
+        bool vlan_mirrored;
+        bool has_mirror;
+        int out_vlan;
+
+        has_mirror = mirror_get(xbridge->mbridge, mirror_mask_ffs(mirrors) - 1,
+                                &vlans, &dup_mirrors, &out, &out_vlan);
+        ovs_assert(has_mirror);
+
+        if (vlans) {
             ctx->xout->wc.masks.vlan_tci |= htons(VLAN_CFI | VLAN_VID_MASK);
         }
+        vlan_mirrored = !vlans || bitmap_is_set(vlans, vlan);
+        free(vlans);
 
-        if (!vlan_is_mirrored(m, vlan)) {
+        if (!vlan_mirrored) {
             mirrors = zero_rightmost_1bit(mirrors);
             continue;
         }
 
-        mirrors &= ~m->dup_mirrors;
-        ctx->xout->mirrors |= m->dup_mirrors;
-        if (m->out) {
-            output_normal(ctx, m->out, vlan);
-        } else if (vlan != m->out_vlan
+        mirrors &= ~dup_mirrors;
+        ctx->xout->mirrors |= dup_mirrors;
+        if (out) {
+            struct xbundle *out_xbundle = xbundle_lookup(out);
+            if (out_xbundle) {
+                output_normal(ctx, out_xbundle, vlan);
+            }
+        } else if (vlan != out_vlan
                    && !eth_addr_is_reserved(orig_flow->dl_dst)) {
-            struct ofbundle *bundle;
+            struct xbundle *xbundle;
 
-            HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
-                if (ofbundle_includes_vlan(bundle, m->out_vlan)
-                    && !bundle->mirror_out) {
-                    output_normal(ctx, bundle, m->out_vlan);
+            LIST_FOR_EACH (xbundle, list_node, &xbridge->xbundles) {
+                if (xbundle_includes_vlan(xbundle, out_vlan)
+                    && !xbundle_mirror_out(xbridge, xbundle)) {
+                    output_normal(ctx, xbundle, out_vlan);
                 }
             }
         }
@@ -266,17 +645,17 @@ add_mirror_actions(struct xlate_ctx *ctx, const struct flow *orig_flow)
 }
 
 /* Given 'vid', the VID obtained from the 802.1Q header that was received as
- * part of a packet (specify 0 if there was no 802.1Q header), and 'in_bundle',
+ * part of a packet (specify 0 if there was no 802.1Q header), and 'in_xbundle',
  * the bundle on which the packet was received, returns the VLAN to which the
  * packet belongs.
  *
  * Both 'vid' and the return value are in the range 0...4095. */
 static uint16_t
-input_vid_to_vlan(const struct ofbundle *in_bundle, uint16_t vid)
+input_vid_to_vlan(const struct xbundle *in_xbundle, uint16_t vid)
 {
-    switch (in_bundle->vlan_mode) {
+    switch (in_xbundle->vlan_mode) {
     case PORT_VLAN_ACCESS:
-        return in_bundle->vlan;
+        return in_xbundle->vlan;
         break;
 
     case PORT_VLAN_TRUNK:
@@ -284,14 +663,14 @@ input_vid_to_vlan(const struct ofbundle *in_bundle, uint16_t vid)
 
     case PORT_VLAN_NATIVE_UNTAGGED:
     case PORT_VLAN_NATIVE_TAGGED:
-        return vid ? vid : in_bundle->vlan;
+        return vid ? vid : in_xbundle->vlan;
 
     default:
         NOT_REACHED();
     }
 }
 
-/* Checks whether a packet with the given 'vid' may ingress on 'in_bundle'.
+/* Checks whether a packet with the given 'vid' may ingress on 'in_xbundle'.
  * If so, returns true.  Otherwise, returns false and, if 'warn' is true, logs
  * a warning.
  *
@@ -299,23 +678,22 @@ input_vid_to_vlan(const struct ofbundle *in_bundle, uint16_t vid)
  * part of a packet (specify 0 if there was no 802.1Q header), in the range
  * 0...4095. */
 static bool
-input_vid_is_valid(uint16_t vid, struct ofbundle *in_bundle, bool warn)
+input_vid_is_valid(uint16_t vid, struct xbundle *in_xbundle, bool warn)
 {
     /* Allow any VID on the OFPP_NONE port. */
-    if (in_bundle == &ofpp_none_bundle) {
+    if (in_xbundle == &ofpp_none_bundle) {
         return true;
     }
 
-    switch (in_bundle->vlan_mode) {
+    switch (in_xbundle->vlan_mode) {
     case PORT_VLAN_ACCESS:
         if (vid) {
             if (warn) {
                 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged "
+                VLOG_WARN_RL(&rl, "dropping VLAN %"PRIu16" tagged "
                              "packet received on port %s configured as VLAN "
-                             "%"PRIu16" access port",
-                             in_bundle->ofproto->up.name, vid,
-                             in_bundle->name, in_bundle->vlan);
+                             "%"PRIu16" access port", vid, in_xbundle->name,
+                             in_xbundle->vlan);
             }
             return false;
         }
@@ -329,14 +707,12 @@ input_vid_is_valid(uint16_t vid, struct ofbundle *in_bundle, bool warn)
         }
         /* Fall through. */
     case PORT_VLAN_TRUNK:
-        if (!ofbundle_includes_vlan(in_bundle, vid)) {
+        if (!xbundle_includes_vlan(in_xbundle, vid)) {
             if (warn) {
                 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" packet "
+                VLOG_WARN_RL(&rl, "dropping VLAN %"PRIu16" packet "
                              "received on port %s not configured for trunking "
-                             "VLAN %"PRIu16,
-                             in_bundle->ofproto->up.name, vid,
-                             in_bundle->name, vid);
+                             "VLAN %"PRIu16, vid, in_xbundle->name, vid);
             }
             return false;
         }
@@ -349,16 +725,16 @@ input_vid_is_valid(uint16_t vid, struct ofbundle *in_bundle, bool warn)
 }
 
 /* Given 'vlan', the VLAN that a packet belongs to, and
- * 'out_bundle', a bundle on which the packet is to be output, returns the VID
+ * 'out_xbundle', a bundle on which the packet is to be output, returns the VID
  * that should be included in the 802.1Q header.  (If the return value is 0,
  * then the 802.1Q header should only be included in the packet if there is a
  * nonzero PCP.)
  *
  * Both 'vlan' and the return value are in the range 0...4095. */
 static uint16_t
-output_vlan_to_vid(const struct ofbundle *out_bundle, uint16_t vlan)
+output_vlan_to_vid(const struct xbundle *out_xbundle, uint16_t vlan)
 {
-    switch (out_bundle->vlan_mode) {
+    switch (out_xbundle->vlan_mode) {
     case PORT_VLAN_ACCESS:
         return 0;
 
@@ -367,7 +743,7 @@ output_vlan_to_vid(const struct ofbundle *out_bundle, uint16_t vlan)
         return vlan;
 
     case PORT_VLAN_NATIVE_UNTAGGED:
-        return vlan == out_bundle->vlan ? 0 : vlan;
+        return vlan == out_xbundle->vlan ? 0 : vlan;
 
     default:
         NOT_REACHED();
@@ -375,21 +751,30 @@ output_vlan_to_vid(const struct ofbundle *out_bundle, uint16_t vlan)
 }
 
 static void
-output_normal(struct xlate_ctx *ctx, const struct ofbundle *out_bundle,
+output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
               uint16_t vlan)
 {
     ovs_be16 *flow_tci = &ctx->xin->flow.vlan_tci;
-    struct ofport_dpif *port;
     uint16_t vid;
     ovs_be16 tci, old_tci;
+    struct xport *xport;
 
-    vid = output_vlan_to_vid(out_bundle, vlan);
-    if (!out_bundle->bond) {
-        port = ofbundle_get_a_port(out_bundle);
+    vid = output_vlan_to_vid(out_xbundle, vlan);
+    if (list_is_empty(&out_xbundle->xports)) {
+        /* Partially configured bundle with no slaves.  Drop the packet. */
+        return;
+    } else if (!out_xbundle->bond) {
+        xport = CONTAINER_OF(list_front(&out_xbundle->xports), struct xport,
+                             bundle_node);
     } else {
-        port = bond_choose_output_slave(out_bundle->bond, &ctx->xin->flow,
-                                        &ctx->xout->wc, vid, &ctx->xout->tags);
-        if (!port) {
+        struct ofport_dpif *ofport;
+
+        ofport = bond_choose_output_slave(out_xbundle->bond, &ctx->xin->flow,
+                                          &ctx->xout->wc, vid,
+                                          &ctx->xout->tags);
+        xport = ofport ? xport_lookup(ofport) : NULL;
+
+        if (!xport) {
             /* No slaves enabled, so drop packet. */
             return;
         }
@@ -397,7 +782,7 @@ output_normal(struct xlate_ctx *ctx, const struct ofbundle *out_bundle,
 
     old_tci = *flow_tci;
     tci = htons(vid);
-    if (tci || out_bundle->use_priority_tags) {
+    if (tci || out_xbundle->use_priority_tags) {
         tci |= *flow_tci & htons(VLAN_PCP_MASK);
         if (tci) {
             tci |= htons(VLAN_CFI);
@@ -405,7 +790,7 @@ output_normal(struct xlate_ctx *ctx, const struct ofbundle *out_bundle,
     }
     *flow_tci = tci;
 
-    compose_output_action(ctx, port->up.ofp_port);
+    compose_output_action(ctx, xport->ofp_port);
     *flow_tci = old_tci;
 }
 
@@ -438,51 +823,51 @@ is_gratuitous_arp(const struct flow *flow, struct flow_wildcards *wc)
 }
 
 static void
-update_learning_table(struct ofproto_dpif *ofproto,
+update_learning_table(const struct xbridge *xbridge,
                       const struct flow *flow, struct flow_wildcards *wc,
-                      int vlan, struct ofbundle *in_bundle)
+                      int vlan, struct xbundle *in_xbundle)
 {
     struct mac_entry *mac;
 
     /* Don't learn the OFPP_NONE port. */
-    if (in_bundle == &ofpp_none_bundle) {
+    if (in_xbundle == &ofpp_none_bundle) {
         return;
     }
 
-    if (!mac_learning_may_learn(ofproto->ml, flow->dl_src, vlan)) {
+    if (!mac_learning_may_learn(xbridge->ml, flow->dl_src, vlan)) {
         return;
     }
 
-    mac = mac_learning_insert(ofproto->ml, flow->dl_src, vlan);
+    mac = mac_learning_insert(xbridge->ml, flow->dl_src, vlan);
     if (is_gratuitous_arp(flow, wc)) {
         /* We don't want to learn from gratuitous ARP packets that are
          * reflected back over bond slaves so we lock the learning table. */
-        if (!in_bundle->bond) {
+        if (!in_xbundle->bond) {
             mac_entry_set_grat_arp_lock(mac);
         } else if (mac_entry_is_grat_arp_locked(mac)) {
             return;
         }
     }
 
-    if (mac_entry_is_new(mac) || mac->port.p != in_bundle) {
+    if (mac_entry_is_new(mac) || mac->port.p != in_xbundle->ofbundle) {
         /* The log messages here could actually be useful in debugging,
          * so keep the rate limit relatively high. */
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
         VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is "
                     "on port %s in VLAN %d",
-                    ofproto->up.name, ETH_ADDR_ARGS(flow->dl_src),
-                    in_bundle->name, vlan);
+                    xbridge->name, ETH_ADDR_ARGS(flow->dl_src),
+                    in_xbundle->name, vlan);
 
-        mac->port.p = in_bundle;
-        mac_learning_changed(ofproto->ml, mac);
+        mac->port.p = in_xbundle->ofbundle;
+        mac_learning_changed(xbridge->ml, mac);
     }
 }
 
-/* Determines whether packets in 'flow' within 'ofproto' should be forwarded or
+/* Determines whether packets in 'flow' within 'xbridge' should be forwarded or
  * dropped.  Returns true if they may be forwarded, false if they should be
  * dropped.
  *
- * 'in_port' must be the ofport_dpif that corresponds to flow->in_port.
+ * 'in_port' must be the xport that corresponds to flow->in_port.
  * 'in_port' must be part of a bundle (e.g. in_port->bundle must be nonnull).
  *
  * 'vlan' must be the VLAN that corresponds to flow->vlan_tci on 'in_port', as
@@ -493,24 +878,24 @@ update_learning_table(struct ofproto_dpif *ofproto,
  * so in one special case.
  */
 static bool
-is_admissible(struct xlate_ctx *ctx, struct ofport_dpif *in_port,
+is_admissible(struct xlate_ctx *ctx, struct xport *in_port,
               uint16_t vlan)
 {
-    struct ofproto_dpif *ofproto = ctx->ofproto;
+    struct xbundle *in_xbundle = in_port->xbundle;
+    const struct xbridge *xbridge = ctx->xbridge;
     struct flow *flow = &ctx->xin->flow;
-    struct ofbundle *in_bundle = in_port->bundle;
 
     /* Drop frames for reserved multicast addresses
      * only if forward_bpdu option is absent. */
-    if (!ofproto->up.forward_bpdu && eth_addr_is_reserved(flow->dl_dst)) {
+    if (!xbridge->forward_bpdu && eth_addr_is_reserved(flow->dl_dst)) {
         xlate_report(ctx, "packet has reserved destination MAC, dropping");
         return false;
     }
 
-    if (in_bundle->bond) {
+    if (in_xbundle->bond) {
         struct mac_entry *mac;
 
-        switch (bond_check_admissibility(in_bundle->bond, in_port,
+        switch (bond_check_admissibility(in_xbundle->bond, in_port->ofport,
                                          flow->dl_dst, &ctx->xout->tags)) {
         case BV_ACCEPT:
             break;
@@ -520,8 +905,8 @@ is_admissible(struct xlate_ctx *ctx, struct ofport_dpif *in_port,
             return false;
 
         case BV_DROP_IF_MOVED:
-            mac = mac_learning_lookup(ofproto->ml, flow->dl_src, vlan, NULL);
-            if (mac && mac->port.p != in_bundle &&
+            mac = mac_learning_lookup(xbridge->ml, flow->dl_src, vlan, NULL);
+            if (mac && mac->port.p != in_xbundle->ofbundle &&
                 (!is_gratuitous_arp(flow, &ctx->xout->wc)
                  || mac_entry_is_grat_arp_locked(mac))) {
                 xlate_report(ctx, "SLB bond thinks this packet looped back, "
@@ -540,8 +925,8 @@ xlate_normal(struct xlate_ctx *ctx)
 {
     struct flow_wildcards *wc = &ctx->xout->wc;
     struct flow *flow = &ctx->xin->flow;
-    struct ofport_dpif *in_port;
-    struct ofbundle *in_bundle;
+    struct xbundle *in_xbundle;
+    struct xport *in_port;
     struct mac_entry *mac;
     uint16_t vlan;
     uint16_t vid;
@@ -552,9 +937,9 @@ xlate_normal(struct xlate_ctx *ctx)
     memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
     wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
 
-    in_bundle = lookup_input_bundle(ctx->ofproto, flow->in_port.ofp_port,
-                                    ctx->xin->packet != NULL, &in_port);
-    if (!in_bundle) {
+    in_xbundle = lookup_input_bundle(ctx->xbridge, flow->in_port.ofp_port,
+                                     ctx->xin->packet != NULL, &in_port);
+    if (!in_xbundle) {
         xlate_report(ctx, "no input bundle, dropping");
         return;
     }
@@ -566,19 +951,19 @@ xlate_normal(struct xlate_ctx *ctx)
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
             VLOG_WARN_RL(&rl, "bridge %s: dropping packet with partial "
                          "VLAN tag received on port %s",
-                         ctx->ofproto->up.name, in_bundle->name);
+                         ctx->xbridge->name, in_xbundle->name);
         }
         xlate_report(ctx, "partial VLAN tag, dropping");
         return;
     }
 
     /* Drop frames on bundles reserved for mirroring. */
-    if (in_bundle->mirror_out) {
+    if (xbundle_mirror_out(ctx->xbridge, in_xbundle)) {
         if (ctx->xin->packet != NULL) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
             VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port "
                          "%s, which is reserved exclusively for mirroring",
-                         ctx->ofproto->up.name, in_bundle->name);
+                         ctx->xbridge->name, in_xbundle->name);
         }
         xlate_report(ctx, "input port is mirror output port, dropping");
         return;
@@ -586,11 +971,11 @@ xlate_normal(struct xlate_ctx *ctx)
 
     /* Check VLAN. */
     vid = vlan_tci_to_vid(flow->vlan_tci);
-    if (!input_vid_is_valid(vid, in_bundle, ctx->xin->packet != NULL)) {
+    if (!input_vid_is_valid(vid, in_xbundle, ctx->xin->packet != NULL)) {
         xlate_report(ctx, "disallowed VLAN VID for this input port, dropping");
         return;
     }
-    vlan = input_vid_to_vlan(in_bundle, vid);
+    vlan = input_vid_to_vlan(in_xbundle, vid);
 
     /* Check other admissibility requirements. */
     if (in_port && !is_admissible(ctx, in_port, vlan)) {
@@ -599,29 +984,32 @@ xlate_normal(struct xlate_ctx *ctx)
 
     /* Learn source MAC. */
     if (ctx->xin->may_learn) {
-        update_learning_table(ctx->ofproto, flow, wc, vlan, in_bundle);
+        update_learning_table(ctx->xbridge, flow, wc, vlan, in_xbundle);
     }
 
     /* Determine output bundle. */
-    mac = mac_learning_lookup(ctx->ofproto->ml, flow->dl_dst, vlan,
+    mac = mac_learning_lookup(ctx->xbridge->ml, flow->dl_dst, vlan,
                               &ctx->xout->tags);
     if (mac) {
-        if (mac->port.p != in_bundle) {
+        struct xbundle *mac_xbundle = xbundle_lookup(mac->port.p);
+        if (mac_xbundle && mac_xbundle != in_xbundle) {
             xlate_report(ctx, "forwarding to learned port");
-            output_normal(ctx, mac->port.p, vlan);
+            output_normal(ctx, mac_xbundle, vlan);
+        } else if (!mac_xbundle) {
+            xlate_report(ctx, "learned port is unknown, dropping");
         } else {
             xlate_report(ctx, "learned port is input port, dropping");
         }
     } else {
-        struct ofbundle *bundle;
+        struct xbundle *xbundle;
 
         xlate_report(ctx, "no learned MAC for destination, flooding");
-        HMAP_FOR_EACH (bundle, hmap_node, &ctx->ofproto->bundles) {
-            if (bundle != in_bundle
-                && ofbundle_includes_vlan(bundle, vlan)
-                && bundle->floodable
-                && !bundle->mirror_out) {
-                output_normal(ctx, bundle, vlan);
+        LIST_FOR_EACH (xbundle, list_node, &ctx->xbridge->xbundles) {
+            if (xbundle != in_xbundle
+                && xbundle_includes_vlan(xbundle, vlan)
+                && xbundle->floodable
+                && !xbundle_mirror_out(ctx->xbridge, xbundle)) {
+                output_normal(ctx, xbundle, vlan);
             }
         }
         ctx->xout->nf_output_iface = NF_OUT_FLOOD;
@@ -633,7 +1021,7 @@ xlate_normal(struct xlate_ctx *ctx)
  * cookie is passed back in the callback for each sampled packet.
  */
 static size_t
-compose_sample_action(const struct ofproto_dpif *ofproto,
+compose_sample_action(const struct xbridge *xbridge,
                       struct ofpbuf *odp_actions,
                       const struct flow *flow,
                       const uint32_t probability,
@@ -648,8 +1036,8 @@ compose_sample_action(const struct ofproto_dpif *ofproto,
     nl_msg_put_u32(odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, probability);
 
     actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS);
-    cookie_offset = put_userspace_action(ofproto, odp_actions, flow, cookie,
-                                         cookie_size);
+    cookie_offset = put_userspace_action(xbridge->ofproto, odp_actions, flow,
+                                         cookie, cookie_size);
 
     nl_msg_end_nested(odp_actions, actions_offset);
     nl_msg_end_nested(odp_actions, sample_offset);
@@ -657,9 +1045,9 @@ compose_sample_action(const struct ofproto_dpif *ofproto,
 }
 
 static void
-compose_sflow_cookie(const struct ofproto_dpif *ofproto,
-                     ovs_be16 vlan_tci, odp_port_t odp_port,
-                     unsigned int n_outputs, union user_action_cookie *cookie)
+compose_sflow_cookie(const struct xbridge *xbridge, ovs_be16 vlan_tci,
+                     odp_port_t odp_port, unsigned int n_outputs,
+                     union user_action_cookie *cookie)
 {
     int ifindex;
 
@@ -675,7 +1063,7 @@ compose_sflow_cookie(const struct ofproto_dpif *ofproto,
         break;
 
     case 1:
-        ifindex = dpif_sflow_odp_port_to_ifindex(ofproto->sflow, odp_port);
+        ifindex = dpif_sflow_odp_port_to_ifindex(xbridge->sflow, odp_port);
         if (ifindex) {
             cookie->sflow.output = ifindex;
             break;
@@ -690,7 +1078,7 @@ compose_sflow_cookie(const struct ofproto_dpif *ofproto,
 
 /* Compose SAMPLE action for sFlow bridge sampling. */
 static size_t
-compose_sflow_action(const struct ofproto_dpif *ofproto,
+compose_sflow_action(const struct xbridge *xbridge,
                      struct ofpbuf *odp_actions,
                      const struct flow *flow,
                      odp_port_t odp_port)
@@ -698,15 +1086,15 @@ compose_sflow_action(const struct ofproto_dpif *ofproto,
     uint32_t probability;
     union user_action_cookie cookie;
 
-    if (!ofproto->sflow || flow->in_port.ofp_port == OFPP_NONE) {
+    if (!xbridge->sflow || flow->in_port.ofp_port == OFPP_NONE) {
         return 0;
     }
 
-    probability = dpif_sflow_get_probability(ofproto->sflow);
-    compose_sflow_cookie(ofproto, htons(0), odp_port,
+    probability = dpif_sflow_get_probability(xbridge->sflow);
+    compose_sflow_cookie(xbridge, htons(0), odp_port,
                          odp_port == ODPP_NONE ? 0 : 1, &cookie);
 
-    return compose_sample_action(ofproto, odp_actions, flow,  probability,
+    return compose_sample_action(xbridge, odp_actions, flow,  probability,
                                  &cookie, sizeof cookie.sflow);
 }
 
@@ -730,21 +1118,21 @@ compose_ipfix_cookie(union user_action_cookie *cookie)
 
 /* Compose SAMPLE action for IPFIX bridge sampling. */
 static void
-compose_ipfix_action(const struct ofproto_dpif *ofproto,
+compose_ipfix_action(const struct xbridge *xbridge,
                      struct ofpbuf *odp_actions,
                      const struct flow *flow)
 {
     uint32_t probability;
     union user_action_cookie cookie;
 
-    if (!ofproto->ipfix || flow->in_port.ofp_port == OFPP_NONE) {
+    if (!xbridge->ipfix || flow->in_port.ofp_port == OFPP_NONE) {
         return;
     }
 
-    probability = dpif_ipfix_get_bridge_exporter_probability(ofproto->ipfix);
+    probability = dpif_ipfix_get_bridge_exporter_probability(xbridge->ipfix);
     compose_ipfix_cookie(&cookie);
 
-    compose_sample_action(ofproto, odp_actions, flow,  probability,
+    compose_sample_action(xbridge, odp_actions, flow,  probability,
                           &cookie, sizeof cookie.ipfix);
 }
 
@@ -754,7 +1142,7 @@ compose_ipfix_action(const struct ofproto_dpif *ofproto,
 static void
 add_sflow_action(struct xlate_ctx *ctx)
 {
-    ctx->user_cookie_offset = compose_sflow_action(ctx->ofproto,
+    ctx->user_cookie_offset = compose_sflow_action(ctx->xbridge,
                                                    &ctx->xout->odp_actions,
                                                    &ctx->xin->flow, ODPP_NONE);
     ctx->sflow_odp_port = 0;
@@ -766,7 +1154,7 @@ add_sflow_action(struct xlate_ctx *ctx)
 static void
 add_ipfix_action(struct xlate_ctx *ctx)
 {
-    compose_ipfix_action(ctx->ofproto, &ctx->xout->odp_actions,
+    compose_ipfix_action(ctx->xbridge, &ctx->xout->odp_actions,
                          &ctx->xin->flow);
 }
 
@@ -787,38 +1175,38 @@ fix_sflow_action(struct xlate_ctx *ctx)
                        sizeof cookie->sflow);
     ovs_assert(cookie->type == USER_ACTION_COOKIE_SFLOW);
 
-    compose_sflow_cookie(ctx->ofproto, base->vlan_tci,
+    compose_sflow_cookie(ctx->xbridge, base->vlan_tci,
                          ctx->sflow_odp_port, ctx->sflow_n_outputs, cookie);
 }
 
 static enum slow_path_reason
 process_special(struct xlate_ctx *ctx, const struct flow *flow,
-                const struct ofport_dpif *ofport, const struct ofpbuf *packet)
+                const struct xport *xport, const struct ofpbuf *packet)
 {
-    struct ofproto_dpif *ofproto = ctx->ofproto;
     struct flow_wildcards *wc = &ctx->xout->wc;
+    const struct xbridge *xbridge = ctx->xbridge;
 
-    if (!ofport) {
+    if (!xport) {
         return 0;
-    } else if (ofport->cfm && cfm_should_process_flow(ofport->cfm, flow, wc)) {
+    } else if (xport->cfm && cfm_should_process_flow(xport->cfm, flow, wc)) {
         if (packet) {
-            cfm_process_heartbeat(ofport->cfm, packet);
+            cfm_process_heartbeat(xport->cfm, packet);
         }
         return SLOW_CFM;
-    } else if (ofport->bfd && bfd_should_process_flow(flow, wc)) {
+    } else if (xport->bfd && bfd_should_process_flow(xport->bfd, flow, wc)) {
         if (packet) {
-            bfd_process_packet(ofport->bfd, flow, packet);
+            bfd_process_packet(xport->bfd, flow, packet);
         }
         return SLOW_BFD;
-    } else if (ofport->bundle && ofport->bundle->lacp
+    } else if (xport->xbundle && xport->xbundle->lacp
                && flow->dl_type == htons(ETH_TYPE_LACP)) {
         if (packet) {
-            lacp_process_packet(ofport->bundle->lacp, ofport, packet);
+            lacp_process_packet(xport->xbundle->lacp, xport->ofport, packet);
         }
         return SLOW_LACP;
-    } else if (ofproto->stp && stp_should_process_flow(flow, wc)) {
+    } else if (xbridge->has_stp && stp_should_process_flow(flow, wc)) {
         if (packet) {
-            stp_process_packet(ofport, packet);
+            stp_process_packet(xport->ofport, packet);
         }
         return SLOW_STP;
     } else {
@@ -830,7 +1218,7 @@ static void
 compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
                         bool check_stp)
 {
-    const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
+    const struct xport *xport = get_ofp_port(ctx->xbridge, ofp_port);
     struct flow_wildcards *wc = &ctx->xout->wc;
     struct flow *flow = &ctx->xin->flow;
     ovs_be16 flow_vlan_tci;
@@ -843,24 +1231,29 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
      * before traversing a patch port. */
     BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
 
-    if (!ofport) {
+    if (!xport) {
         xlate_report(ctx, "Nonexistent output port");
         return;
-    } else if (ofport->up.pp.config & OFPUTIL_PC_NO_FWD) {
+    } else if (xport->config & OFPUTIL_PC_NO_FWD) {
         xlate_report(ctx, "OFPPC_NO_FWD set, skipping output");
         return;
-    } else if (check_stp && !stp_forward_in_state(ofport->stp_state)) {
+    } else if (check_stp && !stp_forward_in_state(xport->stp_state)) {
         xlate_report(ctx, "STP not in forwarding state, skipping output");
         return;
     }
 
-    if (ofport->peer) {
-        struct ofport_dpif *peer = ofport->peer;
+    if (mbridge_has_mirrors(ctx->xbridge->mbridge) && xport->xbundle) {
+        ctx->xout->mirrors |= xbundle_mirror_dst(xport->xbundle->xbridge,
+                                                 xport->xbundle);
+    }
+
+    if (xport->peer) {
+        const struct xport *peer = xport->peer;
         struct flow old_flow = ctx->xin->flow;
         enum slow_path_reason special;
 
-        ctx->ofproto = ofproto_dpif_cast(peer->up.ofproto);
-        flow->in_port.ofp_port = peer->up.ofp_port;
+        ctx->xbridge = peer->xbridge;
+        flow->in_port.ofp_port = peer->ofp_port;
         flow->metadata = htonll(0);
         memset(&flow->tunnel, 0, sizeof flow->tunnel);
         memset(flow->regs, 0, sizeof flow->regs);
@@ -877,18 +1270,20 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
                  * learning action look at the packet, then drop it. */
                 struct flow old_base_flow = ctx->base_flow;
                 size_t old_size = ctx->xout->odp_actions.size;
+                mirror_mask_t old_mirrors = ctx->xout->mirrors;
                 xlate_table_action(ctx, flow->in_port.ofp_port, 0, true);
+                ctx->xout->mirrors = old_mirrors;
                 ctx->base_flow = old_base_flow;
                 ctx->xout->odp_actions.size = old_size;
             }
         }
 
         ctx->xin->flow = old_flow;
-        ctx->ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+        ctx->xbridge = xport->xbundle->xbridge;
 
         if (ctx->xin->resubmit_stats) {
-            netdev_vport_inc_tx(ofport->up.netdev, ctx->xin->resubmit_stats);
-            netdev_vport_inc_rx(peer->up.netdev, ctx->xin->resubmit_stats);
+            netdev_vport_inc_tx(xport->netdev, ctx->xin->resubmit_stats);
+            netdev_vport_inc_rx(peer->netdev, ctx->xin->resubmit_stats);
         }
 
         return;
@@ -898,19 +1293,20 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
     flow_skb_mark = flow->skb_mark;
     flow_nw_tos = flow->nw_tos;
 
-    if (ofproto_dpif_dscp_from_priority(ofport, flow->skb_priority, &dscp)) {
+    if (ofproto_dpif_dscp_from_priority(xport->ofport, flow->skb_priority,
+                                        &dscp)) {
         wc->masks.nw_tos |= IP_ECN_MASK;
         flow->nw_tos &= ~IP_DSCP_MASK;
         flow->nw_tos |= dscp;
     }
 
-    if (ofport->is_tunnel) {
+    if (xport->is_tunnel) {
          /* Save tunnel metadata so that changes made due to
           * the Logical (tunnel) Port are not visible for any further
           * matches, while explicit set actions on tunnel metadata are.
           */
         struct flow_tnl flow_tnl = flow->tunnel;
-        odp_port = tnl_port_send(ofport, flow, &ctx->xout->wc);
+        odp_port = tnl_port_send(xport->ofport, flow, &ctx->xout->wc);
         if (odp_port == ODPP_NONE) {
             xlate_report(ctx, "Tunneling decided against output");
             goto out; /* restore flow_nw_tos */
@@ -920,7 +1316,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
             goto out; /* restore flow_nw_tos */
         }
         if (ctx->xin->resubmit_stats) {
-            netdev_vport_inc_tx(ofport->up.netdev, ctx->xin->resubmit_stats);
+            netdev_vport_inc_tx(xport->netdev, ctx->xin->resubmit_stats);
         }
         out_port = odp_port;
         commit_odp_tunnel_action(flow, &ctx->base_flow,
@@ -929,16 +1325,16 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
     } else {
         ofp_port_t vlandev_port;
 
-        odp_port = ofport->odp_port;
-        if (!hmap_is_empty(&ctx->ofproto->realdev_vid_map)) {
+        odp_port = xport->odp_port;
+        if (ofproto_has_vlan_splinters(ctx->xbridge->ofproto)) {
             wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
         }
-        vlandev_port = vsp_realdev_to_vlandev(ctx->ofproto, ofp_port,
+        vlandev_port = vsp_realdev_to_vlandev(ctx->xbridge->ofproto, ofp_port,
                                               flow->vlan_tci);
         if (vlandev_port == ofp_port) {
             out_port = odp_port;
         } else {
-            out_port = ofp_port_to_odp_port(ctx->ofproto, vlandev_port);
+            out_port = ofp_port_to_odp_port(ctx->xbridge, vlandev_port);
             flow->vlan_tci = htons(0);
         }
         flow->skb_mark &= ~IPSEC_MARK;
@@ -984,7 +1380,7 @@ ctx_rule_hooks(struct xlate_ctx *ctx, struct rule_dpif *rule,
          * OFPTC_TABLE_MISS_DROP
          * When OF1.0, OFPTC_TABLE_MISS_CONTINUE is used. What to do?
          */
-        rule = rule_dpif_miss_rule(ctx->ofproto, &ctx->xin->flow);
+        rule = rule_dpif_miss_rule(ctx->xbridge->ofproto, &ctx->xin->flow);
     }
     if (rule && ctx->xin->resubmit_stats) {
         rule_credit_stats(rule, ctx->xin->resubmit_stats);
@@ -1005,11 +1401,13 @@ xlate_table_action(struct xlate_ctx *ctx,
 
         /* Look up a flow with 'in_port' as the input port. */
         ctx->xin->flow.in_port.ofp_port = in_port;
-        rule = rule_dpif_lookup_in_table(ctx->ofproto, &ctx->xin->flow,
-                                         &ctx->xout->wc, table_id);
+        rule = rule_dpif_lookup_in_table(ctx->xbridge->ofproto,
+                                         &ctx->xin->flow, &ctx->xout->wc,
+                                         table_id);
 
-        ctx->xout->tags |= calculate_flow_tag(ctx->ofproto, &ctx->xin->flow,
-                                              ctx->table_id, rule);
+        ctx->xout->tags |= calculate_flow_tag(ctx->xbridge->ofproto,
+                                              &ctx->xin->flow, ctx->table_id,
+                                              rule);
 
         /* Restore the original input port.  Otherwise OFPP_NORMAL and
          * OFPP_IN_PORT will have surprising behavior. */
@@ -1060,19 +1458,17 @@ xlate_ofpact_resubmit(struct xlate_ctx *ctx,
 static void
 flood_packets(struct xlate_ctx *ctx, bool all)
 {
-    struct ofport_dpif *ofport;
-
-    HMAP_FOR_EACH (ofport, up.hmap_node, &ctx->ofproto->up.ports) {
-        ofp_port_t ofp_port = ofport->up.ofp_port;
+    const struct xport *xport;
 
-        if (ofp_port == ctx->xin->flow.in_port.ofp_port) {
+    HMAP_FOR_EACH (xport, ofp_node, &ctx->xbridge->xports) {
+        if (xport->ofp_port == ctx->xin->flow.in_port.ofp_port) {
             continue;
         }
 
         if (all) {
-            compose_output_action__(ctx, ofp_port, false);
-        } else if (!(ofport->up.pp.config & OFPUTIL_PC_NO_FLOOD)) {
-            compose_output_action(ctx, ofp_port);
+            compose_output_action__(ctx, xport->ofp_port, false);
+        } else if (!(xport->config & OFPUTIL_PC_NO_FLOOD)) {
+            compose_output_action(ctx, xport->ofp_port);
         }
     }
 
@@ -1116,7 +1512,7 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
     pin.send_len = len;
     flow_get_metadata(&ctx->xin->flow, &pin.fmd);
 
-    connmgr_send_packet_in(ctx->ofproto->up.connmgr, &pin);
+    ofproto_dpif_send_packet_in(ctx->xbridge->ofproto, &pin);
     ofpbuf_delete(packet);
 }
 
@@ -1311,7 +1707,8 @@ xlate_enqueue_action(struct xlate_ctx *ctx,
     int error;
 
     /* Translate queue to priority. */
-    error = ofproto_dpif_queue_to_priority(ctx->ofproto, queue_id, &priority);
+    error = ofproto_dpif_queue_to_priority(ctx->xbridge->ofproto, queue_id,
+                                           &priority);
     if (error) {
         /* Fall back to ordinary output action. */
         xlate_output_action(ctx, enqueue->port, 0, false);
@@ -1344,7 +1741,7 @@ xlate_set_queue_action(struct xlate_ctx *ctx, uint32_t queue_id)
 {
     uint32_t skb_priority;
 
-    if (!ofproto_dpif_queue_to_priority(ctx->ofproto, queue_id,
+    if (!ofproto_dpif_queue_to_priority(ctx->xbridge->ofproto, queue_id,
                                         &skb_priority)) {
         ctx->xin->flow.skb_priority = skb_priority;
     } else {
@@ -1354,10 +1751,10 @@ xlate_set_queue_action(struct xlate_ctx *ctx, uint32_t queue_id)
 }
 
 static bool
-slave_enabled_cb(ofp_port_t ofp_port, void *ofproto_)
+slave_enabled_cb(ofp_port_t ofp_port, void *xbridge_)
 {
-    struct ofproto_dpif *ofproto = ofproto_;
-    struct ofport_dpif *port;
+    const struct xbridge *xbridge = xbridge_;
+    struct xport *port;
 
     switch (ofp_port) {
     case OFPP_IN_PORT:
@@ -1370,7 +1767,7 @@ slave_enabled_cb(ofp_port_t ofp_port, void *ofproto_)
     case OFPP_CONTROLLER: /* Not supported by the bundle action. */
         return false;
     default:
-        port = get_ofp_port(ofproto, ofp_port);
+        port = get_ofp_port(xbridge, ofp_port);
         return port ? port->may_enable : false;
     }
 }
@@ -1382,7 +1779,8 @@ xlate_bundle_action(struct xlate_ctx *ctx,
     ofp_port_t port;
 
     port = bundle_execute(bundle, &ctx->xin->flow, &ctx->xout->wc,
-                          slave_enabled_cb, ctx->ofproto);
+                          slave_enabled_cb,
+                          CONST_CAST(struct xbridge *, ctx->xbridge));
     if (bundle->dst.field) {
         nxm_reg_load(&bundle->dst, ofp_to_u16(port), &ctx->xin->flow);
     } else {
@@ -1411,7 +1809,7 @@ xlate_learn_action(struct xlate_ctx *ctx,
     ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
     learn_execute(learn, &ctx->xin->flow, &fm, &ofpacts);
 
-    error = ofproto_flow_mod(&ctx->ofproto->up, &fm);
+    error = ofproto_dpif_flow_mod(ctx->xbridge->ofproto, &fm);
     if (error && !VLOG_DROP_WARN(&rl)) {
         VLOG_WARN("learning action failed to modify flow table (%s)",
                   ofperr_get_name(error));
@@ -1437,6 +1835,10 @@ xlate_fin_timeout(struct xlate_ctx *ctx,
     if (ctx->xin->tcp_flags & (TCP_FIN | TCP_RST) && ctx->rule) {
         struct rule_dpif *rule = ctx->rule;
 
+         if (list_is_empty(&rule->up.expirable)) {
+             list_insert(&rule->up.ofproto->expirable, &rule->up.expirable);
+         }
+
         reduce_timeout(oft->fin_idle_timeout, &rule->up.idle_timeout);
         reduce_timeout(oft->fin_hard_timeout, &rule->up.hard_timeout);
     }
@@ -1456,17 +1858,16 @@ xlate_sample_action(struct xlate_ctx *ctx,
 
   compose_flow_sample_cookie(os->probability, os->collector_set_id,
                              os->obs_domain_id, os->obs_point_id, &cookie);
-  compose_sample_action(ctx->ofproto, &ctx->xout->odp_actions, &ctx->xin->flow,
+  compose_sample_action(ctx->xbridge, &ctx->xout->odp_actions, &ctx->xin->flow,
                         probability, &cookie, sizeof cookie.flow_sample);
 }
 
 static bool
-may_receive(const struct ofport_dpif *port, struct xlate_ctx *ctx)
+may_receive(const struct xport *xport, struct xlate_ctx *ctx)
 {
-    if (port->up.pp.config & (eth_addr_equals(ctx->xin->flow.dl_dst,
-                                              eth_addr_stp)
-                              ? OFPUTIL_PC_NO_RECV_STP
-                              : OFPUTIL_PC_NO_RECV)) {
+    if (xport->config & (eth_addr_equals(ctx->xin->flow.dl_dst, eth_addr_stp)
+                         ? OFPUTIL_PC_NO_RECV_STP
+                         : OFPUTIL_PC_NO_RECV)) {
         return false;
     }
 
@@ -1474,8 +1875,8 @@ may_receive(const struct ofport_dpif *port, struct xlate_ctx *ctx)
      * disabled.  If just learning is enabled, we need to have
      * OFPP_NORMAL and the learning action have a look at the packet
      * before we can drop it. */
-    if (!stp_forward_in_state(port->stp_state)
-            && !stp_learn_in_state(port->stp_state)) {
+    if (!stp_forward_in_state(xport->stp_state)
+        && !stp_learn_in_state(xport->stp_state)) {
         return false;
     }
 
@@ -1723,11 +2124,12 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             ctx->table_id = ogt->table_id;
 
             /* Look up a flow from the new table. */
-            rule = rule_dpif_lookup_in_table(ctx->ofproto, flow, wc,
+            rule = rule_dpif_lookup_in_table(ctx->xbridge->ofproto, flow, wc,
                                              ctx->table_id);
 
-            ctx->xout->tags = calculate_flow_tag(ctx->ofproto, &ctx->xin->flow,
-                                                 ctx->table_id, rule);
+            ctx->xout->tags |= calculate_flow_tag(ctx->xbridge->ofproto,
+                                                  &ctx->xin->flow,
+                                                  ctx->table_id, rule);
 
             rule = ctx_rule_hooks(ctx, rule, true);
 
@@ -1825,7 +2227,7 @@ xlate_out_copy(struct xlate_out *dst, const struct xlate_out *src)
 static bool
 actions_output_to_local_port(const struct xlate_ctx *ctx)
 {
-    odp_port_t local_odp_port = ofp_port_to_odp_port(ctx->ofproto, OFPP_LOCAL);
+    odp_port_t local_odp_port = ofp_port_to_odp_port(ctx->xbridge, OFPP_LOCAL);
     const struct nlattr *a;
     unsigned int left;
 
@@ -1854,12 +2256,12 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
 
     enum slow_path_reason special;
     const struct ofpact *ofpacts;
-    struct ofport_dpif *in_port;
+    struct xport *in_port;
     struct flow orig_flow;
     struct xlate_ctx ctx;
     size_t ofpacts_len;
 
-    COVERAGE_INC(ofproto_dpif_xlate);
+    COVERAGE_INC(xlate_actions);
 
     /* Flow initialization rules:
      * - 'base_flow' must match the kernel's view of the packet at the
@@ -1884,8 +2286,22 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
 
     ctx.xin = xin;
     ctx.xout = xout;
+    ctx.xout->tags = 0;
+    ctx.xout->slow = 0;
+    ctx.xout->has_learn = false;
+    ctx.xout->has_normal = false;
+    ctx.xout->has_fin_timeout = false;
+    ctx.xout->nf_output_iface = NF_OUT_DROP;
+    ctx.xout->mirrors = 0;
+    ofpbuf_use_stub(&ctx.xout->odp_actions, ctx.xout->odp_actions_stub,
+                    sizeof ctx.xout->odp_actions_stub);
+    ofpbuf_reserve(&ctx.xout->odp_actions, NL_A_U32_SIZE);
+
+    ctx.xbridge = xbridge_lookup(xin->ofproto);
+    if (!ctx.xbridge) {
+        return;
+    }
 
-    ctx.ofproto = xin->ofproto;
     ctx.rule = xin->rule;
 
     ctx.base_flow = *flow;
@@ -1901,22 +2317,10 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
     if (tnl_port_should_receive(&ctx.xin->flow)) {
         memset(&wc->masks.tunnel, 0xff, sizeof wc->masks.tunnel);
     }
-    if (xin->ofproto->netflow) {
+    if (ctx.xbridge->has_netflow) {
         netflow_mask_wc(flow, wc);
     }
 
-    ctx.xout->tags = 0;
-    ctx.xout->slow = 0;
-    ctx.xout->has_learn = false;
-    ctx.xout->has_normal = false;
-    ctx.xout->has_fin_timeout = false;
-    ctx.xout->nf_output_iface = NF_OUT_DROP;
-    ctx.xout->mirrors = 0;
-
-    ofpbuf_use_stub(&ctx.xout->odp_actions, ctx.xout->odp_actions_stub,
-                    sizeof ctx.xout->odp_actions_stub);
-    ofpbuf_reserve(&ctx.xout->odp_actions, NL_A_U32_SIZE);
-
     ctx.recurse = 0;
     ctx.max_resubmit_trigger = false;
     ctx.orig_skb_priority = flow->skb_priority;
@@ -1935,14 +2339,14 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
 
     ofpbuf_use_stub(&ctx.stack, ctx.init_stack, sizeof ctx.init_stack);
 
-    if (ctx.ofproto->has_mirrors || hit_resubmit_limit) {
+    if (mbridge_has_mirrors(ctx.xbridge->mbridge) || hit_resubmit_limit) {
         /* Do this conditionally because the copy is expensive enough that it
          * shows up in profiles. */
         orig_flow = *flow;
     }
 
     if (flow->nw_frag & FLOW_NW_FRAG_ANY) {
-        switch (ctx.ofproto->up.frag_handling) {
+        switch (ctx.xbridge->frag) {
         case OFPC_FRAG_NORMAL:
             /* We must pretend that transport ports are unavailable. */
             flow->tp_src = ctx.base_flow.tp_src = htons(0);
@@ -1964,7 +2368,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
         }
     }
 
-    in_port = get_ofp_port(ctx.ofproto, flow->in_port.ofp_port);
+    in_port = get_ofp_port(ctx.xbridge, flow->in_port.ofp_port);
     special = process_special(&ctx, flow, in_port, ctx.xin->packet);
     if (special) {
         ctx.xout->slow = special;
@@ -1973,7 +2377,8 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
         size_t sample_actions_len;
 
         if (flow->in_port.ofp_port
-            != vsp_realdev_to_vlandev(ctx.ofproto, flow->in_port.ofp_port,
+            != vsp_realdev_to_vlandev(ctx.xbridge->ofproto,
+                                      flow->in_port.ofp_port,
                                       flow->vlan_tci)) {
             ctx.base_flow.vlan_tci = 0;
         }
@@ -2000,22 +2405,25 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
             } else if (!VLOG_DROP_ERR(&trace_rl)) {
                 struct ds ds = DS_EMPTY_INITIALIZER;
 
-                ofproto_trace(ctx.ofproto, &orig_flow, ctx.xin->packet, &ds);
+                ofproto_trace(ctx.xbridge->ofproto, &orig_flow,
+                              ctx.xin->packet, &ds);
                 VLOG_ERR("Trace triggered by excessive resubmit "
                          "recursion:\n%s", ds_cstr(&ds));
                 ds_destroy(&ds);
             }
         }
 
-        if (connmgr_has_in_band(ctx.ofproto->up.connmgr)
+        if (ctx.xbridge->has_in_band
             && in_band_must_output_to_local_port(flow)
             && !actions_output_to_local_port(&ctx)) {
             compose_output_action(&ctx, OFPP_LOCAL);
         }
-        if (ctx.ofproto->has_mirrors) {
+
+        fix_sflow_action(&ctx);
+
+        if (mbridge_has_mirrors(ctx.xbridge->mbridge)) {
             add_mirror_actions(&ctx, &orig_flow);
         }
-        fix_sflow_action(&ctx);
     }
 
     ofpbuf_uninit(&ctx.stack);
index f5f0cd0..4cb8530 100644 (file)
 #include "meta-flow.h"
 #include "odp-util.h"
 #include "ofpbuf.h"
+#include "ofproto-dpif-mirror.h"
 #include "ofproto-dpif.h"
 #include "tag.h"
 
+struct bfd;
+struct bond;
+struct lacp;
+struct dpif_ipfix;
+struct dpif_sflow;
+struct mac_learning;
+
 struct xlate_out {
     /* Wildcards relevant in translation.  Any fields that were used to
      * calculate the action must be set for caching and kernel
@@ -103,6 +111,28 @@ struct xlate_in {
     const struct dpif_flow_stats *resubmit_stats;
 };
 
+void xlate_ofproto_set(struct ofproto_dpif *, const char *name,
+                       const struct mac_learning *, const struct mbridge *,
+                       const struct dpif_sflow *, const struct dpif_ipfix *,
+                       enum ofp_config_flags, bool forward_bpdu,
+                       bool has_in_band, bool has_netflow, bool has_stp);
+void xlate_remove_ofproto(struct ofproto_dpif *);
+
+void xlate_bundle_set(struct ofproto_dpif *, struct ofbundle *,
+                      const char *name, enum port_vlan_mode, int vlan,
+                      unsigned long *trunks, bool use_priority_tags,
+                      const struct bond *, const struct lacp *,
+                      bool floodable);
+void xlate_bundle_remove(struct ofbundle *);
+
+void xlate_ofport_set(struct ofproto_dpif *, struct ofbundle *,
+                      struct ofport_dpif *, ofp_port_t, odp_port_t,
+                      const struct netdev *, const struct cfm *,
+                      const struct bfd *, struct ofport_dpif *peer,
+                      enum ofputil_port_config, enum stp_state, bool is_tunnel,
+                      bool may_enable);
+void xlate_ofport_remove(struct ofport_dpif *);
+
 void xlate_actions(struct xlate_in *, struct xlate_out *);
 void xlate_in_init(struct xlate_in *, struct ofproto_dpif *,
                    const struct flow *, struct rule_dpif *,
index 2d42c83..79e23a4 100644 (file)
@@ -50,6 +50,7 @@
 #include "ofp-print.h"
 #include "ofproto-dpif-governor.h"
 #include "ofproto-dpif-ipfix.h"
+#include "ofproto-dpif-mirror.h"
 #include "ofproto-dpif-sflow.h"
 #include "ofproto-dpif-xlate.h"
 #include "poll-loop.h"
@@ -71,6 +72,11 @@ COVERAGE_DEFINE(facet_unexpected);
 COVERAGE_DEFINE(facet_suppress);
 COVERAGE_DEFINE(subfacet_install_fail);
 
+/* Number of implemented OpenFlow tables. */
+enum { N_TABLES = 255 };
+enum { TBL_INTERNAL = N_TABLES - 1 };    /* Used for internal hidden rules. */
+BUILD_ASSERT_DECL(N_TABLES >= 2 && N_TABLES <= 255);
+
 struct flow_miss;
 struct facet;
 
@@ -81,10 +87,25 @@ static struct rule_dpif *rule_dpif_lookup(struct ofproto_dpif *,
 static void rule_get_stats(struct rule *, uint64_t *packets, uint64_t *bytes);
 static void rule_invalidate(const struct rule_dpif *);
 
-static void mirror_destroy(struct ofmirror *);
-static void update_mirror_stats(struct ofproto_dpif *ofproto,
-                                mirror_mask_t mirrors,
-                                uint64_t packets, uint64_t bytes);
+struct ofbundle {
+    struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */
+    struct ofproto_dpif *ofproto; /* Owning ofproto. */
+    void *aux;                  /* Key supplied by ofproto's client. */
+    char *name;                 /* Identifier for log messages. */
+
+    /* Configuration. */
+    struct list ports;          /* Contains "struct ofport"s. */
+    enum port_vlan_mode vlan_mode; /* VLAN mode */
+    int vlan;                   /* -1=trunk port, else a 12-bit VLAN ID. */
+    unsigned long *trunks;      /* Bitmap of trunked VLANs, if 'vlan' == -1.
+                                 * NULL if all VLANs are trunked. */
+    struct lacp *lacp;          /* LACP if LACP is enabled, otherwise NULL. */
+    struct bond *bond;          /* Nonnull iff more than one port. */
+    bool use_priority_tags;     /* Use 802.1p tag for frames in VLAN 0? */
+
+    /* Status. */
+    bool floodable;          /* True if no port has OFPUTIL_PC_NO_FLOOD set. */
+};
 
 static void bundle_remove(struct ofport *);
 static void bundle_update(struct ofbundle *);
@@ -189,8 +210,7 @@ static void subfacet_uninstall(struct subfacet *);
 struct facet {
     /* Owners. */
     struct hmap_node hmap_node;  /* In owning ofproto's 'facets' hmap. */
-    struct list list_node;       /* In owning rule's 'facets' list. */
-    struct rule_dpif *rule;      /* Owning rule. */
+    struct ofproto_dpif *ofproto;
 
     /* Owned data. */
     struct list subfacets;
@@ -226,6 +246,7 @@ struct facet {
     uint8_t tcp_flags;           /* TCP flags seen for this 'rule'. */
 
     struct xlate_out xout;
+    bool fail_open;              /* Facet matched the fail open rule. */
 
     /* Storage for a single subfacet, to reduce malloc() time and space
      * overhead.  (A facet always has at least one subfacet and in the common
@@ -259,6 +280,38 @@ static void push_all_stats(void);
 
 static bool facet_is_controller_flow(struct facet *);
 
+struct ofport_dpif {
+    struct hmap_node odp_port_node; /* In dpif_backer's "odp_to_ofport_map". */
+    struct ofport up;
+
+    odp_port_t odp_port;
+    struct ofbundle *bundle;    /* Bundle that contains this port, if any. */
+    struct list bundle_node;    /* In struct ofbundle's "ports" list. */
+    struct cfm *cfm;            /* Connectivity Fault Management, if any. */
+    struct bfd *bfd;            /* BFD, if any. */
+    tag_type tag;               /* Tag associated with this port. */
+    bool may_enable;            /* May be enabled in bonds. */
+    bool is_tunnel;             /* This port is a tunnel. */
+    long long int carrier_seq;  /* Carrier status changes. */
+    struct ofport_dpif *peer;   /* Peer if patch port. */
+
+    /* Spanning tree. */
+    struct stp_port *stp_port;  /* Spanning Tree Protocol, if any. */
+    enum stp_state stp_state;   /* Always STP_DISABLED if STP not in use. */
+    long long int stp_state_entered;
+
+    struct hmap priorities;     /* Map of attached 'priority_to_dscp's. */
+
+    /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
+     *
+     * This is deprecated.  It is only for compatibility with broken device
+     * drivers in old versions of Linux that do not properly support VLANs when
+     * VLAN devices are not used.  When broken device drivers are no longer in
+     * widespread use, we will delete these interfaces. */
+    ofp_port_t realdev_ofp_port;
+    int vlandev_vid;
+};
+
 /* Node in 'ofport_dpif''s 'priorities' map.  Used to maintain a map from
  * 'priority' (the datapath's term for QoS queue) to the dscp bits which all
  * traffic egressing the 'ofport' with that priority should be marked with. */
@@ -287,8 +340,11 @@ static bool vsp_adjust_flow(const struct ofproto_dpif *, struct flow *);
 static void vsp_remove(struct ofport_dpif *);
 static void vsp_add(struct ofport_dpif *, ofp_port_t realdev_ofp_port, int vid);
 
+static odp_port_t ofp_port_to_odp_port(const struct ofproto_dpif *,
+                                       ofp_port_t);
+
 static ofp_port_t odp_port_to_ofp_port(const struct ofproto_dpif *,
-                                       odp_port_t odp_port);
+                                       odp_port_t);
 
 static struct ofport_dpif *
 ofport_dpif_cast(const struct ofport *ofport)
@@ -310,6 +366,17 @@ struct dpif_completion {
     struct ofoperation *op;
 };
 
+/* Extra information about a classifier table.
+ * Currently used just for optimized flow revalidation. */
+struct table_dpif {
+    /* If either of these is nonnull, then this table has a form that allows
+     * flows to be tagged to avoid revalidating most flows for the most common
+     * kinds of flow table changes. */
+    struct cls_table *catchall_table; /* Table that wildcards all fields. */
+    struct cls_table *other_table;    /* Table with any other wildcard set. */
+    uint32_t basis;                   /* Keeps each table's tags separate. */
+};
+
 /* Reasons that we might need to revalidate every facet, and corresponding
  * coverage counters.
  *
@@ -400,6 +467,57 @@ static struct ofport_dpif *
 odp_port_to_ofport(const struct dpif_backer *, odp_port_t odp_port);
 static void update_moving_averages(struct dpif_backer *backer);
 
+struct ofproto_dpif {
+    struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */
+    struct ofproto up;
+    struct dpif_backer *backer;
+
+    /* Special OpenFlow rules. */
+    struct rule_dpif *miss_rule; /* Sends flow table misses to controller. */
+    struct rule_dpif *no_packet_in_rule; /* Drops flow table misses. */
+    struct rule_dpif *drop_frags_rule; /* Used in OFPC_FRAG_DROP mode. */
+
+    /* Bridging. */
+    struct netflow *netflow;
+    struct dpif_sflow *sflow;
+    struct dpif_ipfix *ipfix;
+    struct hmap bundles;        /* Contains "struct ofbundle"s. */
+    struct mac_learning *ml;
+    bool has_bonded_bundles;
+    struct mbridge *mbridge;
+
+    /* Facets. */
+    struct classifier facets;     /* Contains 'struct facet's. */
+    long long int consistency_rl;
+
+    /* Revalidation. */
+    struct table_dpif tables[N_TABLES];
+
+    /* Support for debugging async flow mods. */
+    struct list completions;
+
+    struct netdev_stats stats; /* To account packets generated and consumed in
+                                * userspace. */
+
+    /* Spanning tree. */
+    struct stp *stp;
+    long long int stp_last_tick;
+
+    /* VLAN splinters. */
+    struct hmap realdev_vid_map; /* (realdev,vid) -> vlandev. */
+    struct hmap vlandev_map;     /* vlandev -> (realdev,vid). */
+
+    /* Ports. */
+    struct sset ports;             /* Set of standard port names. */
+    struct sset ghost_ports;       /* Ports with no datapath port. */
+    struct sset port_poll_set;     /* Queued names for port_poll() reply. */
+    int port_poll_errno;           /* Last errno for port_poll() reply. */
+
+    /* Per ofproto's dpif stats. */
+    uint64_t n_hit;
+    uint64_t n_missed;
+};
+
 /* Defer flow mod completion until "ovs-appctl ofproto/unclog"?  (Useful only
  * for debugging the asynchronous flow_mod implementation.) */
 static bool clogged;
@@ -413,6 +531,16 @@ static struct hmap all_ofproto_dpifs = HMAP_INITIALIZER(&all_ofproto_dpifs);
 
 static void ofproto_dpif_unixctl_init(void);
 
+static inline struct ofproto_dpif *
+ofproto_dpif_cast(const struct ofproto *ofproto)
+{
+    ovs_assert(ofproto->ofproto_class == &ofproto_dpif_class);
+    return CONTAINER_OF(ofproto, struct ofproto_dpif, up);
+}
+
+static struct ofport_dpif *get_ofp_port(const struct ofproto_dpif *ofproto,
+                                        ofp_port_t ofp_port);
+
 /* Upcalls. */
 #define FLOW_MISS_MAX_BATCH 50
 static int handle_upcalls(struct dpif_backer *, unsigned int max_batch);
@@ -431,6 +559,20 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
 /* Initial mappings of port to bridge mappings. */
 static struct shash init_ofp_ports = SHASH_INITIALIZER(&init_ofp_ports);
+
+int
+ofproto_dpif_flow_mod(struct ofproto_dpif *ofproto,
+                      struct ofputil_flow_mod *fm)
+{
+    return ofproto_flow_mod(&ofproto->up, fm);
+}
+
+void
+ofproto_dpif_send_packet_in(struct ofproto_dpif *ofproto,
+                            struct ofputil_packet_in *pin)
+{
+    connmgr_send_packet_in(ofproto->up.connmgr, pin);
+}
 \f
 /* Factory functions. */
 
@@ -639,6 +781,36 @@ type_run(const char *type)
                 continue;
             }
 
+            if (need_revalidate) {
+                struct ofport_dpif *ofport;
+                struct ofbundle *bundle;
+
+                xlate_ofproto_set(ofproto, ofproto->up.name, ofproto->ml,
+                                  ofproto->mbridge, ofproto->sflow,
+                                  ofproto->ipfix, ofproto->up.frag_handling,
+                                  ofproto->up.forward_bpdu,
+                                  connmgr_has_in_band(ofproto->up.connmgr),
+                                  ofproto->netflow != NULL,
+                                  ofproto->stp != NULL);
+
+                HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
+                    xlate_bundle_set(ofproto, bundle, bundle->name,
+                                     bundle->vlan_mode, bundle->vlan,
+                                     bundle->trunks, bundle->use_priority_tags,
+                                     bundle->bond, bundle->lacp,
+                                     bundle->floodable);
+                }
+
+                HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
+                    xlate_ofport_set(ofproto, ofport->bundle, ofport,
+                                     ofport->up.ofp_port, ofport->odp_port,
+                                     ofport->up.netdev, ofport->cfm,
+                                     ofport->bfd, ofport->peer,
+                                     ofport->up.pp.config, ofport->stp_state,
+                                     ofport->is_tunnel, ofport->may_enable);
+                }
+            }
+
             cls_cursor_init(&cursor, &ofproto->facets, NULL);
             CLS_CURSOR_FOR_EACH_SAFE (facet, next, cr, &cursor) {
                 if (need_revalidate
@@ -1028,9 +1200,7 @@ construct(struct ofproto *ofproto_)
     ofproto->stp = NULL;
     hmap_init(&ofproto->bundles);
     ofproto->ml = mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME);
-    for (i = 0; i < MAX_MIRRORS; i++) {
-        ofproto->mirrors[i] = NULL;
-    }
+    ofproto->mbridge = mbridge_create();
     ofproto->has_bonded_bundles = false;
 
     classifier_init(&ofproto->facets);
@@ -1048,7 +1218,6 @@ construct(struct ofproto *ofproto_)
 
     ofproto_dpif_unixctl_init();
 
-    ofproto->has_mirrors = false;
     hmap_init(&ofproto->vlandev_map);
     hmap_init(&ofproto->realdev_vid_map);
 
@@ -1100,6 +1269,7 @@ add_internal_flow(struct ofproto_dpif *ofproto, int id,
     fm.new_cookie = htonll(0);
     fm.cookie = htonll(0);
     fm.cookie_mask = htonll(0);
+    fm.modify_cookie = false;
     fm.table_id = TBL_INTERNAL;
     fm.command = OFPFC_ADD;
     fm.idle_timeout = 0;
@@ -1177,9 +1347,10 @@ destruct(struct ofproto *ofproto_)
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct rule_dpif *rule, *next_rule;
     struct oftable *table;
-    int i;
 
     ofproto->backer->need_revalidate = REV_RECONFIGURE;
+    xlate_remove_ofproto(ofproto);
+
     hmap_remove(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node);
     complete_operations(ofproto);
 
@@ -1192,9 +1363,7 @@ destruct(struct ofproto *ofproto_)
         }
     }
 
-    for (i = 0; i < MAX_MIRRORS; i++) {
-        mirror_destroy(ofproto->mirrors[i]);
-    }
+    mbridge_unref(ofproto->mbridge);
 
     netflow_destroy(ofproto->netflow);
     dpif_sflow_unref(ofproto->sflow);
@@ -1244,6 +1413,11 @@ run(struct ofproto *ofproto_)
         complete_operations(ofproto);
     }
 
+    if (mbridge_need_revalidate(ofproto->mbridge)) {
+        ofproto->backer->need_revalidate = REV_RECONFIGURE;
+        mac_learning_flush(ofproto->ml, NULL);
+    }
+
     /* Do not perform any periodic activity below required by 'ofproto' while
      * waiting for flow restore to complete. */
     if (ofproto_get_flow_restore_wait()) {
@@ -1370,7 +1544,7 @@ flush(struct ofproto *ofproto_)
     n_batch = 0;
     HMAP_FOR_EACH_SAFE (subfacet, next_subfacet, hmap_node,
                         &ofproto->backer->subfacets) {
-        if (ofproto_dpif_cast(subfacet->facet->rule->up.ofproto) != ofproto) {
+        if (subfacet->facet->ofproto != ofproto) {
             continue;
         }
 
@@ -1524,6 +1698,7 @@ port_destruct(struct ofport *port_)
     const char *dp_port_name;
 
     ofproto->backer->need_revalidate = REV_RECONFIGURE;
+    xlate_ofport_remove(port);
 
     dp_port_name = netdev_vport_get_dpif_port(port->up.netdev, namebuf,
                                               sizeof namebuf);
@@ -2131,24 +2306,6 @@ bundle_lookup(const struct ofproto_dpif *ofproto, void *aux)
     return NULL;
 }
 
-/* Looks up each of the 'n_auxes' pointers in 'auxes' as bundles and adds the
- * ones that are found to 'bundles'. */
-static void
-bundle_lookup_multiple(struct ofproto_dpif *ofproto,
-                       void **auxes, size_t n_auxes,
-                       struct hmapx *bundles)
-{
-    size_t i;
-
-    hmapx_init(bundles);
-    for (i = 0; i < n_auxes; i++) {
-        struct ofbundle *bundle = bundle_lookup(ofproto, auxes[i]);
-        if (bundle) {
-            hmapx_add(bundles, bundle);
-        }
-    }
-}
-
 static void
 bundle_update(struct ofbundle *bundle)
 {
@@ -2221,24 +2378,15 @@ bundle_destroy(struct ofbundle *bundle)
 {
     struct ofproto_dpif *ofproto;
     struct ofport_dpif *port, *next_port;
-    int i;
 
     if (!bundle) {
         return;
     }
 
     ofproto = bundle->ofproto;
-    for (i = 0; i < MAX_MIRRORS; i++) {
-        struct ofmirror *m = ofproto->mirrors[i];
-        if (m) {
-            if (m->out == bundle) {
-                mirror_destroy(m);
-            } else if (hmapx_find_and_delete(&m->srcs, bundle)
-                       || hmapx_find_and_delete(&m->dsts, bundle)) {
-                ofproto->backer->need_revalidate = REV_RECONFIGURE;
-            }
-        }
-    }
+    mbridge_unregister_bundle(ofproto->mbridge, bundle->aux);
+
+    xlate_bundle_remove(bundle);
 
     LIST_FOR_EACH_SAFE (port, next_port, bundle_node, &bundle->ports) {
         bundle_del_port(port);
@@ -2293,10 +2441,7 @@ bundle_set(struct ofproto *ofproto_, void *aux,
         bundle->bond = NULL;
 
         bundle->floodable = true;
-
-        bundle->src_mirrors = 0;
-        bundle->dst_mirrors = 0;
-        bundle->mirror_out = 0;
+        mbridge_register_bundle(ofproto->mbridge, bundle);
     }
 
     if (!bundle->name || strcmp(s->name, bundle->name)) {
@@ -2557,238 +2702,45 @@ bundle_wait(struct ofbundle *bundle)
 /* Mirrors. */
 
 static int
-mirror_scan(struct ofproto_dpif *ofproto)
-{
-    int idx;
-
-    for (idx = 0; idx < MAX_MIRRORS; idx++) {
-        if (!ofproto->mirrors[idx]) {
-            return idx;
-        }
-    }
-    return -1;
-}
-
-static struct ofmirror *
-mirror_lookup(struct ofproto_dpif *ofproto, void *aux)
-{
-    int i;
-
-    for (i = 0; i < MAX_MIRRORS; i++) {
-        struct ofmirror *mirror = ofproto->mirrors[i];
-        if (mirror && mirror->aux == aux) {
-            return mirror;
-        }
-    }
-
-    return NULL;
-}
-
-/* Update the 'dup_mirrors' member of each of the ofmirrors in 'ofproto'. */
-static void
-mirror_update_dups(struct ofproto_dpif *ofproto)
-{
-    int i;
-
-    for (i = 0; i < MAX_MIRRORS; i++) {
-        struct ofmirror *m = ofproto->mirrors[i];
-
-        if (m) {
-            m->dup_mirrors = MIRROR_MASK_C(1) << i;
-        }
-    }
-
-    for (i = 0; i < MAX_MIRRORS; i++) {
-        struct ofmirror *m1 = ofproto->mirrors[i];
-        int j;
-
-        if (!m1) {
-            continue;
-        }
-
-        for (j = i + 1; j < MAX_MIRRORS; j++) {
-            struct ofmirror *m2 = ofproto->mirrors[j];
-
-            if (m2 && m1->out == m2->out && m1->out_vlan == m2->out_vlan) {
-                m1->dup_mirrors |= MIRROR_MASK_C(1) << j;
-                m2->dup_mirrors |= m1->dup_mirrors;
-            }
-        }
-    }
-}
-
-static int
-mirror_set(struct ofproto *ofproto_, void *aux,
-           const struct ofproto_mirror_settings *s)
+mirror_set__(struct ofproto *ofproto_, void *aux,
+             const struct ofproto_mirror_settings *s)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    mirror_mask_t mirror_bit;
-    struct ofbundle *bundle;
-    struct ofmirror *mirror;
-    struct ofbundle *out;
-    struct hmapx srcs;          /* Contains "struct ofbundle *"s. */
-    struct hmapx dsts;          /* Contains "struct ofbundle *"s. */
-    int out_vlan;
+    struct ofbundle **srcs, **dsts;
+    int error;
+    size_t i;
 
-    mirror = mirror_lookup(ofproto, aux);
     if (!s) {
-        mirror_destroy(mirror);
+        mirror_destroy(ofproto->mbridge, aux);
         return 0;
     }
-    if (!mirror) {
-        int idx;
-
-        idx = mirror_scan(ofproto);
-        if (idx < 0) {
-            VLOG_WARN("bridge %s: maximum of %d port mirrors reached, "
-                      "cannot create %s",
-                      ofproto->up.name, MAX_MIRRORS, s->name);
-            return EFBIG;
-        }
 
-        mirror = ofproto->mirrors[idx] = xzalloc(sizeof *mirror);
-        mirror->ofproto = ofproto;
-        mirror->idx = idx;
-        mirror->aux = aux;
-        mirror->out_vlan = -1;
-        mirror->name = NULL;
-    }
+    srcs = xmalloc(s->n_srcs * sizeof *srcs);
+    dsts = xmalloc(s->n_dsts * sizeof *dsts);
 
-    if (!mirror->name || strcmp(s->name, mirror->name)) {
-        free(mirror->name);
-        mirror->name = xstrdup(s->name);
+    for (i = 0; i < s->n_srcs; i++) {
+        srcs[i] = bundle_lookup(ofproto, s->srcs[i]);
     }
 
-    /* Get the new configuration. */
-    if (s->out_bundle) {
-        out = bundle_lookup(ofproto, s->out_bundle);
-        if (!out) {
-            mirror_destroy(mirror);
-            return EINVAL;
-        }
-        out_vlan = -1;
-    } else {
-        out = NULL;
-        out_vlan = s->out_vlan;
-    }
-    bundle_lookup_multiple(ofproto, s->srcs, s->n_srcs, &srcs);
-    bundle_lookup_multiple(ofproto, s->dsts, s->n_dsts, &dsts);
-
-    /* If the configuration has not changed, do nothing. */
-    if (hmapx_equals(&srcs, &mirror->srcs)
-        && hmapx_equals(&dsts, &mirror->dsts)
-        && vlan_bitmap_equal(mirror->vlans, s->src_vlans)
-        && mirror->out == out
-        && mirror->out_vlan == out_vlan)
-    {
-        hmapx_destroy(&srcs);
-        hmapx_destroy(&dsts);
-        return 0;
+    for (i = 0; i < s->n_dsts; i++) {
+        dsts[i] = bundle_lookup(ofproto, s->dsts[i]);
     }
 
-    hmapx_swap(&srcs, &mirror->srcs);
-    hmapx_destroy(&srcs);
-
-    hmapx_swap(&dsts, &mirror->dsts);
-    hmapx_destroy(&dsts);
-
-    free(mirror->vlans);
-    mirror->vlans = vlan_bitmap_clone(s->src_vlans);
-
-    mirror->out = out;
-    mirror->out_vlan = out_vlan;
-
-    /* Update bundles. */
-    mirror_bit = MIRROR_MASK_C(1) << mirror->idx;
-    HMAP_FOR_EACH (bundle, hmap_node, &mirror->ofproto->bundles) {
-        if (hmapx_contains(&mirror->srcs, bundle)) {
-            bundle->src_mirrors |= mirror_bit;
-        } else {
-            bundle->src_mirrors &= ~mirror_bit;
-        }
-
-        if (hmapx_contains(&mirror->dsts, bundle)) {
-            bundle->dst_mirrors |= mirror_bit;
-        } else {
-            bundle->dst_mirrors &= ~mirror_bit;
-        }
-
-        if (mirror->out == bundle) {
-            bundle->mirror_out |= mirror_bit;
-        } else {
-            bundle->mirror_out &= ~mirror_bit;
-        }
-    }
-
-    ofproto->backer->need_revalidate = REV_RECONFIGURE;
-    ofproto->has_mirrors = true;
-    mac_learning_flush(ofproto->ml,
-                       &ofproto->backer->revalidate_set);
-    mirror_update_dups(ofproto);
-
-    return 0;
-}
-
-static void
-mirror_destroy(struct ofmirror *mirror)
-{
-    struct ofproto_dpif *ofproto;
-    mirror_mask_t mirror_bit;
-    struct ofbundle *bundle;
-    int i;
-
-    if (!mirror) {
-        return;
-    }
-
-    ofproto = mirror->ofproto;
-    ofproto->backer->need_revalidate = REV_RECONFIGURE;
-    mac_learning_flush(ofproto->ml, &ofproto->backer->revalidate_set);
-
-    mirror_bit = MIRROR_MASK_C(1) << mirror->idx;
-    HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
-        bundle->src_mirrors &= ~mirror_bit;
-        bundle->dst_mirrors &= ~mirror_bit;
-        bundle->mirror_out &= ~mirror_bit;
-    }
-
-    hmapx_destroy(&mirror->srcs);
-    hmapx_destroy(&mirror->dsts);
-    free(mirror->vlans);
-
-    ofproto->mirrors[mirror->idx] = NULL;
-    free(mirror->name);
-    free(mirror);
-
-    mirror_update_dups(ofproto);
-
-    ofproto->has_mirrors = false;
-    for (i = 0; i < MAX_MIRRORS; i++) {
-        if (ofproto->mirrors[i]) {
-            ofproto->has_mirrors = true;
-            break;
-        }
-    }
+    error = mirror_set(ofproto->mbridge, aux, s->name, srcs, s->n_srcs, dsts,
+                       s->n_dsts, s->src_vlans,
+                       bundle_lookup(ofproto, s->out_bundle), s->out_vlan);
+    free(srcs);
+    free(dsts);
+    return error;
 }
 
 static int
-mirror_get_stats(struct ofproto *ofproto_, void *aux,
-                 uint64_t *packets, uint64_t *bytes)
+mirror_get_stats__(struct ofproto *ofproto, void *aux,
+                   uint64_t *packets, uint64_t *bytes)
 {
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    struct ofmirror *mirror = mirror_lookup(ofproto, aux);
-
-    if (!mirror) {
-        *packets = *bytes = UINT64_MAX;
-        return 0;
-    }
-
     push_all_stats();
-
-    *packets = mirror->packet_count;
-    *bytes = mirror->byte_count;
-
-    return 0;
+    return mirror_get_stats(ofproto_dpif_cast(ofproto)->mbridge, aux, packets,
+                            bytes);
 }
 
 static int
@@ -2806,7 +2758,7 @@ is_mirror_output_bundle(const struct ofproto *ofproto_, void *aux)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct ofbundle *bundle = bundle_lookup(ofproto, aux);
-    return bundle && bundle->mirror_out != 0;
+    return bundle && mirror_bundle_out(ofproto->mbridge, bundle) != 0;
 }
 
 static void
@@ -2827,14 +2779,14 @@ set_mac_table_config(struct ofproto *ofproto_, unsigned int idle_time,
 \f
 /* Ports. */
 
-struct ofport_dpif *
+static struct ofport_dpif *
 get_ofp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
 {
     struct ofport *ofport = ofproto_get_port(&ofproto->up, ofp_port);
     return ofport ? ofport_dpif_cast(ofport) : NULL;
 }
 
-struct ofport_dpif *
+static struct ofport_dpif *
 get_odp_port(const struct ofproto_dpif *ofproto, odp_port_t odp_port)
 {
     struct ofport_dpif *port = odp_port_to_ofport(ofproto->backer, odp_port);
@@ -3328,12 +3280,10 @@ init_flow_miss_execute_op(struct flow_miss *miss, struct ofpbuf *packet,
 /* Helper for handle_flow_miss_without_facet() and
  * handle_flow_miss_with_facet(). */
 static void
-handle_flow_miss_common(struct rule_dpif *rule,
-                        struct ofpbuf *packet, const struct flow *flow)
+handle_flow_miss_common(struct ofproto_dpif *ofproto, struct ofpbuf *packet,
+                        const struct flow *flow, bool fail_open)
 {
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
-
-    if (rule->up.cr.priority == FAIL_OPEN_PRIORITY) {
+    if (fail_open) {
         /*
          * Extra-special case for fail-open mode.
          *
@@ -3400,7 +3350,8 @@ handle_flow_miss_without_facet(struct rule_dpif *rule, struct xlate_out *xout,
 
         COVERAGE_INC(facet_suppress);
 
-        handle_flow_miss_common(rule, packet, &miss->flow);
+        handle_flow_miss_common(miss->ofproto, packet, &miss->flow,
+                                rule->up.cr.priority == FAIL_OPEN_PRIORITY);
 
         if (xout->slow) {
             struct xlate_in xin;
@@ -3440,26 +3391,24 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
                             long long int now, struct dpif_flow_stats *stats,
                             struct flow_miss_op *ops, size_t *n_ops)
 {
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
     enum subfacet_path want_path;
     struct subfacet *subfacet;
     struct ofpbuf *packet;
 
-    subfacet = subfacet_create(facet, miss, now);
     want_path = facet->xout.slow ? SF_SLOW_PATH : SF_FAST_PATH;
-    if (stats) {
-        subfacet_update_stats(subfacet, stats);
-    }
 
     LIST_FOR_EACH (packet, list_node, &miss->packets) {
         struct flow_miss_op *op = &ops[*n_ops];
 
-        handle_flow_miss_common(facet->rule, packet, &miss->flow);
+        handle_flow_miss_common(miss->ofproto, packet, &miss->flow,
+                                facet->fail_open);
 
         if (want_path != SF_FAST_PATH) {
+            struct rule_dpif *rule;
             struct xlate_in xin;
 
-            xlate_in_init(&xin, ofproto, &miss->flow, facet->rule, 0, packet);
+            rule = rule_dpif_lookup(facet->ofproto, &facet->flow, NULL);
+            xlate_in_init(&xin, facet->ofproto, &miss->flow, rule, 0, packet);
             xlate_actions_for_side_effects(&xin);
         }
 
@@ -3473,6 +3422,27 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
         }
     }
 
+    /* Don't install the flow if it's the result of the "userspace"
+     * action for an already installed facet.  This can occur when a
+     * datapath flow with wildcards has a "userspace" action and flows
+     * sent to userspace result in a different subfacet, which will then
+     * be rejected as overlapping by the datapath. */
+    if (miss->upcall_type == DPIF_UC_ACTION
+        && !list_is_empty(&facet->subfacets)) {
+        if (stats) {
+            facet->used = MAX(facet->used, stats->used);
+            facet->packet_count += stats->n_packets;
+            facet->byte_count += stats->n_bytes;
+            facet->tcp_flags |= stats->tcp_flags;
+        }
+        return;
+    }
+
+    subfacet = subfacet_create(facet, miss, now);
+    if (stats) {
+        subfacet_update_stats(subfacet, stats);
+    }
+
     if (miss->upcall_type == DPIF_UC_MISS || subfacet->path != want_path) {
         struct flow_miss_op *op = &ops[(*n_ops)++];
         struct dpif_flow_put *put = &op->dpif_op.u.flow_put;
@@ -3498,7 +3468,7 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
             put->actions = facet->xout.odp_actions.data;
             put->actions_len = facet->xout.odp_actions.size;
         } else {
-            compose_slow_path(ofproto, &miss->flow, facet->xout.slow,
+            compose_slow_path(facet->ofproto, &miss->flow, facet->xout.slow,
                               op->slow_stub, sizeof op->slow_stub,
                               &put->actions, &put->actions_len);
         }
@@ -3807,6 +3777,18 @@ handle_miss_upcalls(struct dpif_backer *backer, struct dpif_upcall *upcalls,
 
             COVERAGE_INC(subfacet_install_fail);
 
+            /* Zero-out subfacet counters when installation failed, but
+             * datapath reported hits.  This should not happen and
+             * indicates a bug, since if the datapath flow exists, we
+             * should not be attempting to create a new subfacet.  A
+             * buggy datapath could trigger this, so just zero out the
+             * counters and log an error. */
+            if (subfacet->dp_packet_count || subfacet->dp_byte_count) {
+                VLOG_ERR_RL(&rl, "failed to install subfacet for which "
+                            "datapath reported hits");
+                subfacet->dp_packet_count = subfacet->dp_byte_count = 0;
+            }
+
             subfacet->path = SF_NOT_INSTALLED;
         }
 
@@ -4085,7 +4067,6 @@ update_subfacet_stats(struct subfacet *subfacet,
                       const struct dpif_flow_stats *stats)
 {
     struct facet *facet = subfacet->facet;
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
     struct dpif_flow_stats diff;
 
     diff.tcp_flags = stats->tcp_flags;
@@ -4105,7 +4086,7 @@ update_subfacet_stats(struct subfacet *subfacet,
         diff.n_bytes = 0;
     }
 
-    ofproto->n_hit += diff.n_packets;
+    facet->ofproto->n_hit += diff.n_packets;
     subfacet->dp_packet_count = stats->n_packets;
     subfacet->dp_byte_count = stats->n_bytes;
     subfacet_update_stats(subfacet, &diff);
@@ -4323,7 +4304,6 @@ expire_subfacets(struct dpif_backer *backer, int dp_max_idle)
 static void
 rule_expire(struct rule_dpif *rule)
 {
-    struct facet *facet, *next_facet;
     long long int now;
     uint8_t reason;
 
@@ -4346,12 +4326,6 @@ rule_expire(struct rule_dpif *rule)
 
     COVERAGE_INC(ofproto_dpif_expired);
 
-    /* Update stats.  (This is a no-op if the rule expired due to an idle
-     * timeout, because that only happens when the rule has no facets left.) */
-    LIST_FOR_EACH_SAFE (facet, next_facet, list_node, &rule->facets) {
-        facet_remove(facet);
-    }
-
     /* Get rid of the rule. */
     ofproto_rule_expire(&rule->up, reason);
 }
@@ -4378,15 +4352,14 @@ facet_create(const struct flow_miss *miss, struct rule_dpif *rule,
     struct match match;
 
     facet = xzalloc(sizeof *facet);
+    facet->ofproto = miss->ofproto;
     facet->packet_count = facet->prev_packet_count = stats->n_packets;
     facet->byte_count = facet->prev_byte_count = stats->n_bytes;
     facet->tcp_flags = stats->tcp_flags;
     facet->used = stats->used;
     facet->flow = miss->flow;
     facet->learn_rl = time_msec() + 500;
-    facet->rule = rule;
 
-    list_push_back(&facet->rule->facets, &facet->list_node);
     list_init(&facet->subfacets);
     netflow_flow_init(&facet->nf_flow);
     netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, facet->used);
@@ -4398,6 +4371,7 @@ facet_create(const struct flow_miss *miss, struct rule_dpif *rule,
     classifier_insert(&ofproto->facets, &facet->cr);
 
     facet->nf_flow.output_iface = facet->xout.nf_output_iface;
+    facet->fail_open = rule->up.cr.priority == FAIL_OPEN_PRIORITY;
 
     return facet;
 }
@@ -4441,7 +4415,6 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow,
 static void
 facet_remove(struct facet *facet)
 {
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
     struct subfacet *subfacet, *next_subfacet;
 
     ovs_assert(!list_is_empty(&facet->subfacets));
@@ -4463,9 +4436,8 @@ facet_remove(struct facet *facet)
                         &facet->subfacets) {
         subfacet_destroy__(subfacet);
     }
-    classifier_remove(&ofproto->facets, &facet->cr);
+    classifier_remove(&facet->ofproto->facets, &facet->cr);
     cls_rule_destroy(&facet->cr);
-    list_remove(&facet->list_node);
     facet_free(facet);
 }
 
@@ -4495,13 +4467,12 @@ facet_learn(struct facet *facet)
 static void
 facet_account(struct facet *facet)
 {
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
     const struct nlattr *a;
     unsigned int left;
     ovs_be16 vlan_tci;
     uint64_t n_bytes;
 
-    if (!facet->xout.has_normal || !ofproto->has_bonded_bundles) {
+    if (!facet->xout.has_normal || !facet->ofproto->has_bonded_bundles) {
         return;
     }
     n_bytes = facet->byte_count - facet->accounted_bytes;
@@ -4522,7 +4493,7 @@ facet_account(struct facet *facet)
 
         switch (nl_attr_type(a)) {
         case OVS_ACTION_ATTR_OUTPUT:
-            port = get_odp_port(ofproto, nl_attr_get_odp_port(a));
+            port = get_odp_port(facet->ofproto, nl_attr_get_odp_port(a));
             if (port && port->bundle && port->bundle->bond) {
                 bond_account(port->bundle->bond, &facet->flow,
                              vlan_tci_to_vid(vlan_tci), n_bytes);
@@ -4548,9 +4519,11 @@ static bool
 facet_is_controller_flow(struct facet *facet)
 {
     if (facet) {
-        const struct rule *rule = &facet->rule->up;
-        const struct ofpact *ofpacts = rule->ofpacts;
-        size_t ofpacts_len = rule->ofpacts_len;
+        struct ofproto_dpif *ofproto = facet->ofproto;
+        const struct rule_dpif *rule = rule_dpif_lookup(ofproto, &facet->flow,
+                                                        NULL);
+        const struct ofpact *ofpacts = rule->up.ofpacts;
+        size_t ofpacts_len = rule->up.ofpacts_len;
 
         if (ofpacts_len > 0 &&
             ofpacts->type == OFPACT_CONTROLLER &&
@@ -4568,7 +4541,7 @@ facet_is_controller_flow(struct facet *facet)
 static void
 facet_flush_stats(struct facet *facet)
 {
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+    struct ofproto_dpif *ofproto = facet->ofproto;
     struct subfacet *subfacet;
 
     LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
@@ -4637,41 +4610,21 @@ facet_check_consistency(struct facet *facet)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 15);
 
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
-
     struct xlate_out xout;
     struct xlate_in xin;
 
     struct rule_dpif *rule;
-    bool ok;
-
-    /* Check the rule for consistency. */
-    rule = rule_dpif_lookup(ofproto, &facet->flow, NULL);
-    if (rule != facet->rule) {
-        if (!VLOG_DROP_WARN(&rl)) {
-            struct ds s = DS_EMPTY_INITIALIZER;
-
-            flow_format(&s, &facet->flow);
-            ds_put_format(&s, ": facet associated with wrong rule (was "
-                          "table=%"PRIu8",", facet->rule->up.table_id);
-            cls_rule_format(&facet->rule->up.cr, &s);
-            ds_put_format(&s, ") (should have been table=%"PRIu8",",
-                          rule->up.table_id);
-            cls_rule_format(&rule->up.cr, &s);
-            ds_put_char(&s, ')');
-
-            VLOG_WARN("%s", ds_cstr(&s));
-            ds_destroy(&s);
-        }
-        return false;
-    }
+    bool ok, fail_open;
 
     /* Check the datapath actions for consistency. */
-    xlate_in_init(&xin, ofproto, &facet->flow, rule, 0, NULL);
+    rule = rule_dpif_lookup(facet->ofproto, &facet->flow, NULL);
+    xlate_in_init(&xin, facet->ofproto, &facet->flow, rule, 0, NULL);
     xlate_actions(&xin, &xout);
 
+    fail_open = rule->up.cr.priority == FAIL_OPEN_PRIORITY;
     ok = ofpbuf_equal(&facet->xout.odp_actions, &xout.odp_actions)
-        && facet->xout.slow == xout.slow;
+        && facet->xout.slow == xout.slow
+        && facet->fail_open == fail_open;
     if (!ok && !VLOG_DROP_WARN(&rl)) {
         struct ds s = DS_EMPTY_INITIALIZER;
 
@@ -4692,7 +4645,10 @@ facet_check_consistency(struct facet *facet)
             ds_put_format(&s, " slow path incorrect. should be %d", xout.slow);
         }
 
-        VLOG_WARN("%s", ds_cstr(&s));
+        if (facet->fail_open != fail_open) {
+            ds_put_format(&s, " fail open incorrect. should be %s",
+                          fail_open ? "true" : "false");
+        }
         ds_destroy(&s);
     }
     xlate_out_uninit(&xout);
@@ -4715,7 +4671,7 @@ facet_check_consistency(struct facet *facet)
 static bool
 facet_revalidate(struct facet *facet)
 {
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+    struct ofproto_dpif *ofproto = facet->ofproto;
     struct rule_dpif *new_rule;
     struct subfacet *subfacet;
     struct flow_wildcards wc;
@@ -4796,15 +4752,8 @@ facet_revalidate(struct facet *facet)
     facet->xout.nf_output_iface = xout.nf_output_iface;
     facet->xout.mirrors = xout.mirrors;
     facet->nf_flow.output_iface = facet->xout.nf_output_iface;
-
-    if (facet->rule != new_rule) {
-        COVERAGE_INC(facet_changed_rule);
-        list_remove(&facet->list_node);
-        list_push_back(&new_rule->facets, &facet->list_node);
-        facet->rule = new_rule;
-        facet->used = new_rule->up.created;
-        facet->prev_used = facet->used;
-    }
+    facet->used = MAX(facet->used, new_rule->up.created);
+    facet->fail_open = new_rule->up.cr.priority == FAIL_OPEN_PRIORITY;
 
     xlate_out_uninit(&xout);
     return true;
@@ -4835,10 +4784,9 @@ facet_push_stats(struct facet *facet, bool may_learn)
     stats.tcp_flags = facet->tcp_flags;
 
     if (may_learn || stats.n_packets || facet->used > facet->prev_used) {
-        struct ofproto_dpif *ofproto =
-            ofproto_dpif_cast(facet->rule->up.ofproto);
-
+        struct ofproto_dpif *ofproto = facet->ofproto;
         struct ofport_dpif *in_port;
+        struct rule_dpif *rule;
         struct xlate_in xin;
 
         facet->prev_packet_count = facet->packet_count;
@@ -4850,15 +4798,16 @@ facet_push_stats(struct facet *facet, bool may_learn)
             netdev_vport_inc_rx(in_port->up.netdev, &stats);
         }
 
-        rule_credit_stats(facet->rule, &stats);
+        rule = rule_dpif_lookup(ofproto, &facet->flow, NULL);
+        rule_credit_stats(rule, &stats);
         netflow_flow_update_time(ofproto->netflow, &facet->nf_flow,
                                  facet->used);
         netflow_flow_update_flags(&facet->nf_flow, facet->tcp_flags);
-        update_mirror_stats(ofproto, facet->xout.mirrors, stats.n_packets,
-                            stats.n_bytes);
+        mirror_update_stats(ofproto->mbridge, facet->xout.mirrors,
+                            stats.n_packets, stats.n_bytes);
 
-        xlate_in_init(&xin, ofproto, &facet->flow, facet->rule,
-                      stats.tcp_flags, NULL);
+        xlate_in_init(&xin, ofproto, &facet->flow, rule, stats.tcp_flags,
+                      NULL);
         xin.resubmit_stats = &stats;
         xin.may_learn = may_learn;
         xlate_actions_for_side_effects(&xin);
@@ -4981,7 +4930,7 @@ static void
 subfacet_destroy__(struct subfacet *subfacet)
 {
     struct facet *facet = subfacet->facet;
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+    struct ofproto_dpif *ofproto = facet->ofproto;
 
     /* Update ofproto stats before uninstall the subfacet. */
     ofproto->backer->subfacet_del_count++;
@@ -5047,7 +4996,6 @@ subfacet_install(struct subfacet *subfacet, const struct ofpbuf *odp_actions,
                  struct dpif_flow_stats *stats)
 {
     struct facet *facet = subfacet->facet;
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
     enum subfacet_path path = facet->xout.slow ? SF_SLOW_PATH : SF_FAST_PATH;
     const struct nlattr *actions = odp_actions->data;
     size_t actions_len = odp_actions->size;
@@ -5064,7 +5012,7 @@ subfacet_install(struct subfacet *subfacet, const struct ofpbuf *odp_actions,
     }
 
     if (path == SF_SLOW_PATH) {
-        compose_slow_path(ofproto, &facet->flow, facet->xout.slow,
+        compose_slow_path(facet->ofproto, &facet->flow, facet->xout.slow,
                           slow_path_stub, sizeof slow_path_stub,
                           &actions, &actions_len);
     }
@@ -5096,8 +5044,7 @@ static void
 subfacet_uninstall(struct subfacet *subfacet)
 {
     if (subfacet->path != SF_NOT_INSTALLED) {
-        struct rule_dpif *rule = subfacet->facet->rule;
-        struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+        struct ofproto_dpif *ofproto = subfacet->facet->ofproto;
         struct dpif_flow_stats stats;
         int error;
 
@@ -5268,28 +5215,8 @@ rule_construct(struct rule *rule_)
     rule->packet_count = 0;
     rule->byte_count = 0;
 
-    victim = rule_dpif_cast(ofoperation_get_victim(rule->up.pending));
-    if (victim && !list_is_empty(&victim->facets)) {
-        struct facet *facet;
-
-        rule->facets = victim->facets;
-        list_moved(&rule->facets);
-        LIST_FOR_EACH (facet, list_node, &rule->facets) {
-            /* XXX: We're only clearing our local counters here.  It's possible
-             * that quite a few packets are unaccounted for in the datapath
-             * statistics.  These will be accounted to the new rule instead of
-             * cleared as required.  This could be fixed by clearing out the
-             * datapath statistics for this facet, but currently it doesn't
-             * seem worth it. */
-            facet_reset_counters(facet);
-            facet->rule = rule;
-        }
-    } else {
-        /* Must avoid list_moved() in this case. */
-        list_init(&rule->facets);
-    }
-
     table_id = rule->up.table_id;
+    victim = rule_dpif_cast(ofoperation_get_victim(rule->up.pending));
     if (victim) {
         rule->tag = victim->tag;
     } else if (table_id == 0) {
@@ -5307,16 +5234,9 @@ rule_construct(struct rule *rule_)
 }
 
 static void
-rule_destruct(struct rule *rule_)
+rule_destruct(struct rule *rule)
 {
-    struct rule_dpif *rule = rule_dpif_cast(rule_);
-    struct facet *facet, *next_facet;
-
-    LIST_FOR_EACH_SAFE (facet, next_facet, list_node, &rule->facets) {
-        facet_revalidate(facet);
-    }
-
-    complete_operation(rule);
+    complete_operation(rule_dpif_cast(rule));
 }
 
 static void
@@ -5482,35 +5402,6 @@ put_userspace_action(const struct ofproto_dpif *ofproto,
     return odp_put_userspace_action(pid, cookie, cookie_size, odp_actions);
 }
 
-
-static void
-update_mirror_stats(struct ofproto_dpif *ofproto, mirror_mask_t mirrors,
-                    uint64_t packets, uint64_t bytes)
-{
-    if (!mirrors) {
-        return;
-    }
-
-    for (; mirrors; mirrors = zero_rightmost_1bit(mirrors)) {
-        struct ofmirror *m;
-
-        m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1];
-
-        if (!m) {
-            /* In normal circumstances 'm' will not be NULL.  However,
-             * if mirrors are reconfigured, we can temporarily get out
-             * of sync in facet_revalidate().  We could "correct" the
-             * mirror list before reaching here, but doing that would
-             * not properly account the traffic stats we've currently
-             * accumulated for previous mirror configuration. */
-            continue;
-        }
-
-        m->packet_count += packets;
-        m->byte_count += bytes;
-    }
-}
-
 tag_type
 calculate_flow_tag(struct ofproto_dpif *ofproto, const struct flow *flow,
                    uint8_t table_id, struct rule_dpif *rule)
@@ -5793,6 +5684,13 @@ ofproto_unixctl_fdb_flush(struct unixctl_conn *conn, int argc,
     unixctl_command_reply(conn, "table successfully flushed");
 }
 
+static struct ofport_dpif *
+ofbundle_get_a_port(const struct ofbundle *bundle)
+{
+    return CONTAINER_OF(list_front(&bundle->ports), struct ofport_dpif,
+                        bundle_node);
+}
+
 static void
 ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
                          const char *argv[], void *aux OVS_UNUSED)
@@ -6457,12 +6355,21 @@ ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn,
 
     HMAP_FOR_EACH (subfacet, hmap_node, &ofproto->backer->subfacets) {
         struct facet *facet = subfacet->facet;
+        struct odputil_keybuf maskbuf;
+        struct ofpbuf mask;
 
-        if (ofproto_dpif_cast(facet->rule->up.ofproto) != ofproto) {
+        if (facet->ofproto != ofproto) {
             continue;
         }
 
-        odp_flow_key_format(subfacet->key, subfacet->key_len, &ds);
+        ofpbuf_use_stack(&mask, &maskbuf, sizeof maskbuf);
+        if (enable_megaflows) {
+            odp_flow_key_from_mask(&mask, &facet->xout.wc.masks,
+                                   &facet->flow, UINT32_MAX);
+        }
+
+        odp_flow_format(subfacet->key, subfacet->key_len,
+                        mask.data, mask.size, &ds);
 
         ds_put_format(&ds, ", packets:%"PRIu64", bytes:%"PRIu64", used:",
                       subfacet->dp_packet_count, subfacet->dp_byte_count);
@@ -6602,6 +6509,12 @@ hash_realdev_vid(ofp_port_t realdev_ofp_port, int vid)
     return hash_2words(ofp_to_u16(realdev_ofp_port), vid);
 }
 
+bool
+ofproto_has_vlan_splinters(const struct ofproto_dpif *ofproto)
+{
+    return !hmap_is_empty(&ofproto->realdev_vid_map);
+}
+
 /* Returns the OFP port number of the Linux VLAN device that corresponds to
  * 'vlan_tci' on the network device with port number 'realdev_ofp_port' in
  * 'struct ofport_dpif'.  For example, given 'realdev_ofp_port' of eth0 and
@@ -6739,7 +6652,7 @@ vsp_add(struct ofport_dpif *port, ofp_port_t realdev_ofp_port, int vid)
     }
 }
 
-odp_port_t
+static odp_port_t
 ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
 {
     const struct ofport_dpif *ofport = get_ofp_port(ofproto, ofp_port);
@@ -6880,8 +6793,8 @@ const struct ofproto_class ofproto_dpif_class = {
     set_queues,
     bundle_set,
     bundle_remove,
-    mirror_set,
-    mirror_get_stats,
+    mirror_set__,
+    mirror_get_stats__,
     set_flood_vlans,
     is_mirror_output_bundle,
     forward_bpdu_changed,
index 0704297..b220423 100644 (file)
 #include "util.h"
 
 union user_action_cookie;
-
-#define MAX_MIRRORS 32
-typedef uint32_t mirror_mask_t;
-#define MIRROR_MASK_C(X) UINT32_C(X)
-BUILD_ASSERT_DECL(sizeof(mirror_mask_t) * CHAR_BIT >= MAX_MIRRORS);
-
-/* Number of implemented OpenFlow tables. */
-enum { N_TABLES = 255 };
-enum { TBL_INTERNAL = N_TABLES - 1 };    /* Used for internal hidden rules. */
-BUILD_ASSERT_DECL(N_TABLES >= 2 && N_TABLES <= 255);
+struct ofproto_dpif;
+struct ofport_dpif;
 
 struct rule_dpif {
     struct rule up;
@@ -55,149 +47,6 @@ struct rule_dpif {
     uint64_t byte_count;         /* Number of bytes received. */
 
     tag_type tag;                /* Caches rule_calculate_tag() result. */
-
-    struct list facets;          /* List of "struct facet"s. */
-};
-
-/* Extra information about a classifier table.
- * Currently used just for optimized flow revalidation. */
-struct table_dpif {
-    /* If either of these is nonnull, then this table has a form that allows
-     * flows to be tagged to avoid revalidating most flows for the most common
-     * kinds of flow table changes. */
-    struct cls_table *catchall_table; /* Table that wildcards all fields. */
-    struct cls_table *other_table;    /* Table with any other wildcard set. */
-    uint32_t basis;                   /* Keeps each table's tags separate. */
-};
-
-struct ofproto_dpif {
-    struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */
-    struct ofproto up;
-    struct dpif_backer *backer;
-
-    /* Special OpenFlow rules. */
-    struct rule_dpif *miss_rule; /* Sends flow table misses to controller. */
-    struct rule_dpif *no_packet_in_rule; /* Drops flow table misses. */
-    struct rule_dpif *drop_frags_rule; /* Used in OFPC_FRAG_DROP mode. */
-
-    /* Bridging. */
-    struct netflow *netflow;
-    struct dpif_sflow *sflow;
-    struct dpif_ipfix *ipfix;
-    struct hmap bundles;        /* Contains "struct ofbundle"s. */
-    struct mac_learning *ml;
-    struct ofmirror *mirrors[MAX_MIRRORS];
-    bool has_mirrors;
-    bool has_bonded_bundles;
-
-    /* Facets. */
-    struct classifier facets;     /* Contains 'struct facet's. */
-    long long int consistency_rl;
-
-    /* Revalidation. */
-    struct table_dpif tables[N_TABLES];
-
-    /* Support for debugging async flow mods. */
-    struct list completions;
-
-    struct netdev_stats stats; /* To account packets generated and consumed in
-                                * userspace. */
-
-    /* Spanning tree. */
-    struct stp *stp;
-    long long int stp_last_tick;
-
-    /* VLAN splinters. */
-    struct hmap realdev_vid_map; /* (realdev,vid) -> vlandev. */
-    struct hmap vlandev_map;     /* vlandev -> (realdev,vid). */
-
-    /* Ports. */
-    struct sset ports;             /* Set of standard port names. */
-    struct sset ghost_ports;       /* Ports with no datapath port. */
-    struct sset port_poll_set;     /* Queued names for port_poll() reply. */
-    int port_poll_errno;           /* Last errno for port_poll() reply. */
-
-    /* Per ofproto's dpif stats. */
-    uint64_t n_hit;
-    uint64_t n_missed;
-};
-
-struct ofport_dpif {
-    struct hmap_node odp_port_node; /* In dpif_backer's "odp_to_ofport_map". */
-    struct ofport up;
-
-    odp_port_t odp_port;
-    struct ofbundle *bundle;    /* Bundle that contains this port, if any. */
-    struct list bundle_node;    /* In struct ofbundle's "ports" list. */
-    struct cfm *cfm;            /* Connectivity Fault Management, if any. */
-    struct bfd *bfd;            /* BFD, if any. */
-    tag_type tag;               /* Tag associated with this port. */
-    bool may_enable;            /* May be enabled in bonds. */
-    bool is_tunnel;             /* This port is a tunnel. */
-    long long int carrier_seq;  /* Carrier status changes. */
-    struct ofport_dpif *peer;   /* Peer if patch port. */
-
-    /* Spanning tree. */
-    struct stp_port *stp_port;  /* Spanning Tree Protocol, if any. */
-    enum stp_state stp_state;   /* Always STP_DISABLED if STP not in use. */
-    long long int stp_state_entered;
-
-    struct hmap priorities;     /* Map of attached 'priority_to_dscp's. */
-
-    /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
-     *
-     * This is deprecated.  It is only for compatibility with broken device
-     * drivers in old versions of Linux that do not properly support VLANs when
-     * VLAN devices are not used.  When broken device drivers are no longer in
-     * widespread use, we will delete these interfaces. */
-    ofp_port_t realdev_ofp_port;
-    int vlandev_vid;
-};
-
-struct ofbundle {
-    struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */
-    struct ofproto_dpif *ofproto; /* Owning ofproto. */
-    void *aux;                  /* Key supplied by ofproto's client. */
-    char *name;                 /* Identifier for log messages. */
-
-    /* Configuration. */
-    struct list ports;          /* Contains "struct ofport"s. */
-    enum port_vlan_mode vlan_mode; /* VLAN mode */
-    int vlan;                   /* -1=trunk port, else a 12-bit VLAN ID. */
-    unsigned long *trunks;      /* Bitmap of trunked VLANs, if 'vlan' == -1.
-                                 * NULL if all VLANs are trunked. */
-    struct lacp *lacp;          /* LACP if LACP is enabled, otherwise NULL. */
-    struct bond *bond;          /* Nonnull iff more than one port. */
-    bool use_priority_tags;     /* Use 802.1p tag for frames in VLAN 0? */
-
-    /* Status. */
-    bool floodable;          /* True if no port has OFPUTIL_PC_NO_FLOOD set. */
-
-    /* Port mirroring info. */
-    mirror_mask_t src_mirrors;  /* Mirrors triggered when packet received. */
-    mirror_mask_t dst_mirrors;  /* Mirrors triggered when packet sent. */
-    mirror_mask_t mirror_out;   /* Mirrors that output to this bundle. */
-};
-
-struct ofmirror {
-    struct ofproto_dpif *ofproto; /* Owning ofproto. */
-    size_t idx;                 /* In ofproto's "mirrors" array. */
-    void *aux;                  /* Key supplied by ofproto's client. */
-    char *name;                 /* Identifier for log messages. */
-
-    /* Selection criteria. */
-    struct hmapx srcs;          /* Contains "struct ofbundle *"s. */
-    struct hmapx dsts;          /* Contains "struct ofbundle *"s. */
-    unsigned long *vlans;       /* Bitmap of chosen VLANs, NULL selects all. */
-
-    /* Output (exactly one of out == NULL and out_vlan == -1 is true). */
-    struct ofbundle *out;       /* Output port or NULL. */
-    int out_vlan;               /* Output VLAN or -1. */
-    mirror_mask_t dup_mirrors;  /* Bitmap of mirrors with the same output. */
-
-    /* Counters. */
-    int64_t packet_count;       /* Number of packets sent. */
-    int64_t byte_count;         /* Number of bytes sent. */
 };
 
 static inline struct rule_dpif *rule_dpif_cast(const struct rule *rule)
@@ -205,36 +54,6 @@ static inline struct rule_dpif *rule_dpif_cast(const struct rule *rule)
     return rule ? CONTAINER_OF(rule, struct rule_dpif, up) : NULL;
 }
 
-static inline struct ofproto_dpif *
-ofproto_dpif_cast(const struct ofproto *ofproto)
-{
-    ovs_assert(ofproto->ofproto_class == &ofproto_dpif_class);
-    return CONTAINER_OF(ofproto, struct ofproto_dpif, up);
-}
-
-static inline struct ofport_dpif *
-ofbundle_get_a_port(const struct ofbundle *bundle)
-{
-    return CONTAINER_OF(list_front(&bundle->ports), struct ofport_dpif,
-                        bundle_node);
-}
-
-static inline int
-mirror_mask_ffs(mirror_mask_t mask)
-{
-    BUILD_ASSERT_DECL(sizeof(unsigned int) >= sizeof(mask));
-    return ffs(mask);
-}
-
-struct ofport_dpif *get_ofp_port(const struct ofproto_dpif *,
-                                 ofp_port_t ofp_port);
-
-struct ofport_dpif *get_odp_port(const struct ofproto_dpif *,
-                                        odp_port_t odp_port);
-
-odp_port_t ofp_port_to_odp_port(const struct ofproto_dpif *,
-                              ofp_port_t ofp_port);
-
 struct rule_dpif *rule_dpif_lookup_in_table(struct ofproto_dpif *,
                                             const struct flow *,
                                             struct flow_wildcards *,
@@ -260,6 +79,7 @@ bool stp_should_process_flow(const struct flow *, struct flow_wildcards *);
 void stp_process_packet(const struct ofport_dpif *,
                         const struct ofpbuf *packet);
 
+bool ofproto_has_vlan_splinters(const struct ofproto_dpif *);
 ofp_port_t vsp_realdev_to_vlandev(const struct ofproto_dpif *,
                                   ofp_port_t realdev_ofp_port,
                                   ovs_be16 vlan_tci);
@@ -271,4 +91,8 @@ int ofproto_dpif_queue_to_priority(const struct ofproto_dpif *,
 tag_type calculate_flow_tag(struct ofproto_dpif *, const struct flow *,
                             uint8_t table_id, struct rule_dpif *);
 
+void ofproto_dpif_send_packet_in(struct ofproto_dpif *,
+                                 struct ofputil_packet_in *pin);
+int ofproto_dpif_flow_mod(struct ofproto_dpif *, struct ofputil_flow_mod *);
+
 #endif /* ofproto-dpif.h */
index 522c839..9119235 100644 (file)
@@ -213,6 +213,7 @@ static uint64_t pick_datapath_id(const struct ofproto *);
 static uint64_t pick_fallback_dpid(void);
 static void ofproto_destroy__(struct ofproto *);
 static void update_mtu(struct ofproto *, struct ofport *);
+static void meter_delete(struct ofproto *, uint32_t first, uint32_t last);
 
 /* unixctl. */
 static void ofproto_unixctl_init(void);
@@ -1085,6 +1086,11 @@ ofproto_destroy__(struct ofproto *ofproto)
     ovs_assert(list_is_empty(&ofproto->pending));
     ovs_assert(!ofproto->n_pending);
 
+    if (ofproto->meters) {
+        meter_delete(ofproto, 1, ofproto->meter_features.max_meters);
+        free(ofproto->meters);
+    }
+
     connmgr_destroy(ofproto->connmgr);
 
     hmap_remove(&all_ofprotos, &ofproto->hmap_node);
@@ -3475,7 +3481,7 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
 
         op = ofoperation_create(group, rule, OFOPERATION_MODIFY, 0);
 
-        if (fm->new_cookie != htonll(UINT64_MAX)) {
+        if (fm->modify_cookie && fm->new_cookie != htonll(UINT64_MAX)) {
             ofproto_rule_change_cookie(ofproto, rule, fm->new_cookie);
         }
         if (actions_changed) {
@@ -4255,6 +4261,22 @@ meter_create(const struct ofputil_meter_config *config,
     return meter;
 }
 
+static void
+meter_delete(struct ofproto *ofproto, uint32_t first, uint32_t last)
+{
+    uint32_t mid;
+    for (mid = first; mid <= last; ++mid) {
+        struct meter *meter = ofproto->meters[mid];
+        if (meter) {
+            ofproto->meters[mid] = NULL;
+            ofproto->ofproto_class->meter_del(ofproto,
+                                              meter->provider_meter_id);
+            free(meter->bands);
+            free(meter);
+        }
+    }
+}
+
 static enum ofperr
 handle_add_meter(struct ofproto *ofproto, struct ofputil_meter_mod *mm)
 {
@@ -4335,16 +4357,7 @@ handle_delete_meter(struct ofconn *ofconn, const struct ofp_header *oh,
     }
 
     /* Delete the meters. */
-    for (meter_id = first; meter_id <= last; ++meter_id) {
-        struct meter *meter = ofproto->meters[meter_id];
-        if (meter) {
-            ofproto->meters[meter_id] = NULL;
-            ofproto->ofproto_class->meter_del(ofproto,
-                                              meter->provider_meter_id);
-            free(meter->bands);
-            free(meter);
-        }
-    }
+    meter_delete(ofproto, first, last);
 
     return 0;
 }
@@ -4458,7 +4471,7 @@ handle_meter_request(struct ofconn *ofconn, const struct ofp_header *request,
         if (!meter) {
             continue; /* Skip non-existing meters. */
         }
-        if (type == OFPTYPE_METER_REQUEST) {
+        if (type == OFPTYPE_METER_STATS_REQUEST) {
             struct ofputil_meter_stats stats;
 
             stats.meter_id = meter_id;
@@ -4590,20 +4603,20 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
         return handle_flow_monitor_request(ofconn, oh);
 
-    case OFPTYPE_METER_REQUEST:
-    case OFPTYPE_METER_CONFIG_REQUEST:
+    case OFPTYPE_METER_STATS_REQUEST:
+    case OFPTYPE_METER_CONFIG_STATS_REQUEST:
         return handle_meter_request(ofconn, oh, type);
 
-    case OFPTYPE_METER_FEATURES_REQUEST:
+    case OFPTYPE_METER_FEATURES_STATS_REQUEST:
         return handle_meter_features_request(ofconn, oh);
 
         /* FIXME: Change the following once they are implemented: */
     case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
     case OFPTYPE_GET_ASYNC_REQUEST:
-    case OFPTYPE_GROUP_REQUEST:
-    case OFPTYPE_GROUP_DESC_REQUEST:
-    case OFPTYPE_GROUP_FEATURES_REQUEST:
-    case OFPTYPE_TABLE_FEATURES_REQUEST:
+    case OFPTYPE_GROUP_STATS_REQUEST:
+    case OFPTYPE_GROUP_DESC_STATS_REQUEST:
+    case OFPTYPE_GROUP_FEATURES_STATS_REQUEST:
+    case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
         return OFPERR_OFPBRC_BAD_TYPE;
 
     case OFPTYPE_HELLO:
@@ -4627,13 +4640,13 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_FLOW_MONITOR_RESUMED:
     case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
     case OFPTYPE_GET_ASYNC_REPLY:
-    case OFPTYPE_GROUP_REPLY:
-    case OFPTYPE_GROUP_DESC_REPLY:
-    case OFPTYPE_GROUP_FEATURES_REPLY:
-    case OFPTYPE_METER_REPLY:
-    case OFPTYPE_METER_CONFIG_REPLY:
-    case OFPTYPE_METER_FEATURES_REPLY:
-    case OFPTYPE_TABLE_FEATURES_REPLY:
+    case OFPTYPE_GROUP_STATS_REPLY:
+    case OFPTYPE_GROUP_DESC_STATS_REPLY:
+    case OFPTYPE_GROUP_FEATURES_STATS_REPLY:
+    case OFPTYPE_METER_STATS_REPLY:
+    case OFPTYPE_METER_CONFIG_STATS_REPLY:
+    case OFPTYPE_METER_FEATURES_STATS_REPLY:
+    case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
     default:
         return OFPERR_OFPBRC_BAD_TYPE;
     }
index 1201e6f..4628b59 100644 (file)
@@ -176,7 +176,6 @@ the command line or through the \fBovsdb\-server/add\-db\fR command.
 .so lib/vlog-unixctl.man
 .so lib/memory-unixctl.man
 .so lib/coverage-unixctl.man
-.so lib/stress-unixctl.man
 .SH "SEE ALSO"
 .
 .BR ovsdb\-tool (1).
index 912f599..ab44b3a 100644 (file)
@@ -45,7 +45,6 @@
 #include "shash.h"
 #include "stream-ssl.h"
 #include "stream.h"
-#include "stress.h"
 #include "sset.h"
 #include "table.h"
 #include "timeval.h"
@@ -137,7 +136,6 @@ main(int argc, char *argv[])
 
     proctitle_init(argc, argv);
     set_program_name(argv[0]);
-    stress_init_command();
     signal(SIGPIPE, SIG_IGN);
     process_init();
 
index dc40403..27a3b03 100644 (file)
@@ -174,6 +174,7 @@ systemctl start openvswitch.service
 %doc /usr/share/man/man8/ovs-test.8.gz
 %doc /usr/share/man/man8/ovs-l3ping.8.gz
 /var/lib/openvswitch
+/var/log/openvswitch
 /usr/share/openvswitch/scripts/ovs-ctl
 %exclude /etc/openvswitch
 %exclude /usr/bin/ovs-benchmark
index 53512bc..0fd5200 100644 (file)
@@ -155,3 +155,4 @@ exit 0
 /usr/share/doc/openvswitch-%{version}/FAQ
 /usr/share/doc/openvswitch-%{version}/README.RHEL
 /var/lib/openvswitch
+/var/log/openvswitch
index c6f589e..2b1049a 100644 (file)
@@ -9,6 +9,7 @@
 /ovs-pki.log
 /pki/
 /test-aes128
+/test-atomic
 /test-bundle
 /test-byte-order
 /test-classifier
index ec1c347..7e781c3 100644 (file)
@@ -46,7 +46,7 @@ table=0 actions=learn(table=1,hard_timeout=10, NXM_OF_VLAN_TCI[0..11],output:NXM
 table=1 priority=0 actions=flood
 ]])
 AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
-[[usable protocols: OXM,OpenFlow10+table_id,NXM+table_id
+[[usable protocols: OXM,OpenFlow10+table_id,NXM+table_id,OpenFlow11
 chosen protocol: OpenFlow10+table_id
 OFPT_FLOW_MOD (xid=0x1): ADD table:255 actions=learn(table=1,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
 OFPT_FLOW_MOD (xid=0x2): ADD table:255 actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[])
@@ -75,11 +75,13 @@ AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(load:5->NXM_OF_IP_DST[])']],
   [1], [], [stderr])
 AT_CHECK([sed -e 's/.*|meta_flow|WARN|//' < stderr], [0],
   [[destination field ip_dst lacks correct prerequisites
+ovs-ofctl: actions are invalid with specified match (OFPBAC_BAD_ARGUMENT)
 ]], [[]])
 AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(load:NXM_OF_IP_DST[]->NXM_NX_REG1[])']],
   [1], [], [stderr])
 AT_CHECK([sed -e 's/.*|meta_flow|WARN|//' < stderr], [0],
   [[source field ip_dst lacks correct prerequisites
+ovs-ofctl: actions are invalid with specified match (OFPBAC_BAD_ARGUMENT)
 ]])
 AT_CLEANUP
 
index f84a55b..ded0a22 100644 (file)
@@ -21,7 +21,7 @@ AT_CHECK([test-hmap], [0], [.........
 AT_CLEANUP
 
 AT_SETUP([test hash index])
-AT_CHECK([test-hindex], [0], [..................
+AT_CHECK([test-hindex], [0], [.....................
 ])
 AT_CLEANUP
 
@@ -112,6 +112,8 @@ AT_CLEANUP
 m4_foreach(
   [testname],
   [[ctz],
+   [round_up_pow2],
+   [round_down_pow2],
    [popcount],
    [log_2_floor],
    [bitwise_copy],
index f8290a1..22886fc 100644 (file)
@@ -475,11 +475,11 @@ AT_CHECK([ovs-ofctl ofp-print "\
 03 0a 00 4c 00 00 00 00 ff ff ff 00 00 2a 00 00 \
 00 01 00 0c 80 00 00 04 ff ff ff fe 00 00 00 00 \
 00 00 ff ff ff ff ff ff 00 23 20 83 c1 5f 80 35 \
-00 01 08 00 06 04 00 03 00 23 20 83 c1 5f 00 00 \
+00 01 08 00 06 04 00 01 00 23 20 83 c1 5f 00 00 \
 00 00 00 23 20 83 c1 5f 00 00 00 00 \
 "], [0], [dnl
 OFPT_PACKET_IN (OF1.2) (xid=0x0): total_len=42 in_port=LOCAL (via no_match) data_len=42 buffer=0xffffff00
-rarp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:23:20:83:c1:5f,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_op=3,arp_sha=00:23:20:83:c1:5f,arp_tha=00:23:20:83:c1:5f
+rarp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:23:20:83:c1:5f,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_op=1,arp_sha=00:23:20:83:c1:5f,arp_tha=00:23:20:83:c1:5f
 ])
 AT_CLEANUP
 
@@ -699,6 +699,26 @@ ofp_util|INFO|post: arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_ds
 ])
 AT_CLEANUP
 
+# The flow is formatted with cls_rule_format() for the low-verbosity case.
+AT_SETUP([OFPT_FLOW_MOD - OF1.1 - low verbosity])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
+020e 0090 01020304 \
+da1aa3e035d87158 ffffffffffffffff \
+02 01 003c 0078 9c40 ffffffff ffffffff ffffffff 0003 \
+0000 \
+\
+0000 0058 00000000 000003f7 \
+000000000000ffffffffffff 000000000000ffffffffffff \
+0000 00 00 0806 00 00 c0a88000000000ff 00000000ffffffff 0000 0000 \
+00000000 00 000000 0000000000000000ffffffffffffffff \
+\
+0001 0008 03 000000 \
+" 2], [0], [dnl
+OFPT_FLOW_MOD (OF1.1) (xid=0x1020304): MOD table:2 priority=40000,arp,arp_spa=192.168.128.0/24 cookie:0xda1aa3e035d87158/0xffffffffffffffff idle:60 hard:120 send_flow_rem check_overlap actions=goto_table:3
+])
+AT_CLEANUP
+
 # The flow is formatted with cls_rule_format() for the low-verbosity case.
 AT_SETUP([OFPT_FLOW_MOD - OF1.2 - low verbosity])
 AT_KEYWORDS([ofp-print])
index 88492cf..c3c1c64 100644 (file)
@@ -166,7 +166,7 @@ AT_SETUP([ofproto-dpif - DSCP])
 OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy])
 ADD_OF_PORTS([br0], [9])
 AT_DATA([flows.txt], [dnl
-actions=output:65534,enqueue:1:1,enqueue:1:2,enqueue:1:2,enqueue:1:1,output:1,mod_nw_tos:0,output:1,output:65534
+actions=output:LOCAL,enqueue:1:1,enqueue:1:2,enqueue:1:2,enqueue:1:1,output:1,mod_nw_tos:0,output:1,output:LOCAL
 ])
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-vsctl -- \
@@ -197,8 +197,8 @@ AT_DATA([flows.txt], [dnl
 in_port=local actions=local,flood
 in_port=1 actions=flood
 in_port=2 actions=all
-in_port=3 actions=output:65534,output:1,output:2,output:3,output:4,output:5,output:6,output:7
-in_port=4 actions=enqueue:65534:1,enqueue:1:1,enqueue:2:1,enqueue:3:2,enqueue:4:1,enqueue:5:1,enqueue:6:1,enqueue:7:1
+in_port=3 actions=output:LOCAL,output:1,output:2,output:3,output:4,output:5,output:6,output:7
+in_port=4 actions=enqueue:LOCAL:1,enqueue:1:1,enqueue:2:1,enqueue:3:2,enqueue:4:1,enqueue:5:1,enqueue:6:1,enqueue:7:1
 ])
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-ofctl mod-port br0 5 noforward])
@@ -251,6 +251,7 @@ AT_SETUP([ofproto-dpif - controller])
 OVS_VSWITCHD_START([dnl
    add-port br0 p1 -- set Interface p1 type=dummy
 ])
+ON_EXIT([kill `cat ovs-ofctl.pid`])
 
 AT_CAPTURE_FILE([ofctl_monitor.log])
 AT_DATA([flows.txt], [dnl
@@ -265,6 +266,7 @@ cookie=0x6 table=4 in_port=83 actions=load:4->NXM_NX_REG3[[]],mod_nw_src:83.83.8
 cookie=0x7 table=5 in_port=84 actions=load:5->NXM_NX_REG4[[]],load:6->NXM_NX_TUN_ID[[]],mod_nw_dst:84.84.84.84,controller,resubmit(85,6)
 cookie=0x8 table=6 in_port=85 actions=mod_tp_src:85,controller,resubmit(86,7)
 cookie=0x9 table=7 in_port=86 actions=mod_tp_dst:86,controller,controller
+cookie=0xa dl_src=40:44:44:44:44:41 actions=push_vlan:0x8100,mod_vlan_vid:99,mod_vlan_pcp:1,controller
 cookie=0xa dl_src=40:44:44:44:44:42 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller
 cookie=0xa dl_src=40:44:44:44:44:43 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller
 cookie=0xa dl_src=40:44:44:44:44:44 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller
@@ -284,8 +286,9 @@ AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --no-chdir --pidfil
 for i in 1 2 3 ; do
     ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'
 done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
 
-OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
 tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=9 tcp_csum:0
@@ -303,8 +306,9 @@ AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --no-chdir --pidfil
 for i in 1 2 3 ; do
     ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=10:11:11:11:11:11,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=10)'
 done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
 
-OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
 tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0
@@ -322,8 +326,9 @@ AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --no-chdir --pidfil
 for i in 1 2 3 ; do
     ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=30:33:33:33:33:33,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=10)'
 done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
 
-OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
 tcp,metadata=0,in_port=0,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0
@@ -335,14 +340,35 @@ OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuf
 tcp,metadata=0,in_port=0,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0
 ])
 
+dnl Modified VLAN controller action.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:41,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+tcp,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+tcp,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+tcp,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0
+])
+
 dnl Modified MPLS controller action.
 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
 
 for i in 1 2 3; do
     ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:42,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
 done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
 
-OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
 mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:42,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
@@ -381,8 +407,9 @@ AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor
 for i in 1 2 3; do
     ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:44,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))'
 done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
 
-OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
 mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=7,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
@@ -400,8 +427,9 @@ AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor
 for i in 1 2 3; do
     ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:45,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
 done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
 
-OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
 mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:45,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=63,mpls_bos=1
@@ -419,8 +447,9 @@ AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor
 for i in 1 2 3; do
     ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:46,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
 done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
 
-OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
 mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:46,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=10,mpls_bos=1
@@ -438,8 +467,9 @@ AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor
 for i in 1 2 3; do
     ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:47,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
 done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
 
-OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
 mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:47,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=10,mpls_bos=1
@@ -457,8 +487,9 @@ AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor
 for i in 1 2 3; do
     ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:48,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
 done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
 
-OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
 mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:48,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=9,mpls_bos=1
@@ -476,8 +507,9 @@ AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor
 for i in 1 2 3; do
     ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:55:55:55:55:55,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=100,tc=7,ttl=64,bos=1)'
 done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
 
-OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
 mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:55:55:55:55:55,dl_dst=50:54:00:00:00:07,mpls_label=1000,mpls_tc=7,mpls_ttl=64,mpls_bos=1
@@ -495,8 +527,9 @@ AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor
 for i in 1 2 3; do
     ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=70:77:77:77:77:77,dst=50:54:00:00:00:07),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'
 done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
 
-OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
 mplsm,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=70:77:77:77:77:77,dl_dst=50:54:00:00:00:07,mpls_label=1000,mpls_tc=7,mpls_ttl=64,mpls_bos=1
@@ -522,8 +555,9 @@ done
 #for i in 2 3; do
 #    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=60:66:66:66:66:66,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=10,tc=3,ttl=100,bos=1)'
 #done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
 
-OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered)
 tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=80,tp_dst=0 tcp_csum:7744
@@ -541,8 +575,9 @@ AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --no-chdir --pidfile 2> of
 for i in 1 ; do
     ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=20:22:22:22:22:22,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=11)'
 done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 18])
+ovs-appctl -t ovs-ofctl exit
 
-OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
 tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11 tcp_csum:0
@@ -578,8 +613,9 @@ AT_CHECK([ovs-ofctl monitor br0 65534 --detach --no-chdir --pidfile 2> ofctl_mon
 for i in 1 ; do
     ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 20 22 22 22 22 22 08 00 45 00 00 1C 00 00 00 00 00 11 00 00 C0 A8 00 01 C0 A8 00 02 00 08 00 0B 00 00 12 34 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00'
 done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 18])
+ovs-appctl -t ovs-ofctl exit
 
-OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
 udp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:1234
@@ -620,6 +656,7 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
  cookie=0x7, table=5, n_packets=2, n_bytes=120, in_port=84 actions=load:0x5->NXM_NX_REG4[[]],load:0x6->NXM_NX_TUN_ID[[]],mod_nw_dst:84.84.84.84,CONTROLLER:65535,resubmit(85,6)
  cookie=0x8, table=6, n_packets=2, n_bytes=120, in_port=85 actions=mod_tp_src:85,CONTROLLER:65535,resubmit(86,7)
  cookie=0x9, table=7, n_packets=2, n_bytes=120, in_port=86 actions=mod_tp_dst:86,CONTROLLER:65535,CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:41 actions=mod_vlan_vid:99,mod_vlan_pcp:1,CONTROLLER:65535
  cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:42 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
  cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:43 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
  cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:44 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
@@ -2051,12 +2088,12 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:
 AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | sort | STRIP_USED], [0], [dnl
-in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
-in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
+in_port(1),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0x2),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
+in_port(2),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0x2),icmp(type=0/0,code=0/0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
 ])
 
 AT_CHECK([ovs-appctl dpif/dump-flows br1 | sort | STRIP_USED], [0], [dnl
-in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
+in_port(3),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0x2),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
 ])
 
 OVS_VSWITCHD_STOP
@@ -2073,12 +2110,12 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:
 AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | sort | STRIP_USED], [0], [dnl
-in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
-in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
+in_port(1),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0x2),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
+in_port(2),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0x2),icmp(type=0/0,code=0/0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
 ])
 
 AT_CHECK([ovs-appctl dpif/dump-flows br1 | sort | STRIP_USED], [0], [dnl
-in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
+in_port(3),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0x2),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
 ])
 
 AT_CHECK([ovs-appctl dpif/del-flows br0])
@@ -2086,7 +2123,7 @@ AT_CHECK([ovs-appctl dpif/dump-flows br0 | sort | STRIP_USED], [0], [dnl
 ])
 
 AT_CHECK([ovs-appctl dpif/dump-flows br1 | sort | STRIP_USED], [0], [dnl
-in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
+in_port(3),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0x2),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
 ])
 
 OVS_VSWITCHD_STOP
@@ -2133,10 +2170,10 @@ dummy@ovs-dummy: hit:13 missed:2
 ])
 
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_USED], [0], [dnl
-in_port(100),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:9, bytes:540, used:0.0s, actions:101,3,2
+in_port(100),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0x2),icmp(type=8/0,code=0/0), packets:9, bytes:540, used:0.0s, actions:101,3,2
 ]),
 AT_CHECK([ovs-appctl dpif/dump-flows br1 | STRIP_USED], [0], [dnl
-in_port(101),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:4, bytes:240, used:0.0s, actions:100,2,3
+in_port(101),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0x2),icmp(type=8/0,code=0/0), packets:4, bytes:240, used:0.0s, actions:100,2,3
 ])
 
 AT_CHECK([ovs-ofctl dump-ports br0 pbr0], [0], [dnl
index d034105..839d41f 100644 (file)
@@ -83,7 +83,7 @@ m4_define([OVS_VSWITCHD_START],
 /ofproto|INFO|datapath ID changed to fedcba9876543210/d']])
 
    dnl Add bridges, ports, etc.
-   AT_CHECK([ovs-vsctl -- add-br br0 -- set bridge br0 datapath-type=dummy other-config:datapath-id=fedcba9876543210 other-config:hwaddr=aa:55:aa:55:00:00 protocols=[[OpenFlow10,OpenFlow12,OpenFlow13]] fail-mode=secure -- $1 m4_if([$2], [], [], [| ${PERL} $srcdir/uuidfilt.pl])], [0], [$2])
+   AT_CHECK([ovs-vsctl -- add-br br0 -- set bridge br0 datapath-type=dummy other-config:datapath-id=fedcba9876543210 other-config:hwaddr=aa:55:aa:55:00:00 protocols=[[OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13]] fail-mode=secure -- $1 m4_if([$2], [], [], [| ${PERL} $srcdir/uuidfilt.pl])], [0], [$2])
 ])
 
 m4_divert_push([PREPARE_TESTS])
index 53d5cad..e2e6f1b 100644 (file)
@@ -253,6 +253,28 @@ AT_CHECK([ovs-ofctl -F openflow10 dump-flows br0 | ofctl_strip], [0], [OFPST_FLO
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto - basic flow_mod commands (OpenFlow 1.1)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip], [0], [OFPST_FLOW reply (OF1.1):
+])
+AT_CHECK([echo 'in_port=2,actions=1' | ovs-ofctl -O OpenFlow11 add-flows br0 -])
+AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 in_port=1,actions=2])
+AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 table=1,in_port=4,actions=3])
+AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ in_port=1 actions=output:2
+ in_port=2 actions=output:1
+ table=1, in_port=4 actions=output:3
+OFPST_FLOW reply (OF1.1):
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 dump-aggregate br0 table=0 | STRIP_XIDS], [0], [dnl
+OFPST_AGGREGATE reply (OF1.1): packet_count=0 byte_count=0 flow_count=2
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 del-flows br0])
+AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip], [0], [OFPST_FLOW reply (OF1.1):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto - set-field flow_mod commands (NXM)])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl add-flow br0 ipv6,table=1,in_port=3,actions=drop])
@@ -339,6 +361,21 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto - no mod flow with cookie change (OpenFlow 1.1)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 cookie=0x1,in_port=1,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:1
+OFPST_FLOW reply (OF1.1):
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 mod-flows br0 cookie=0x2,in_port=1,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:1
+OFPST_FLOW reply (OF1.1):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 dnl The OpenFlow 1.2 spec states that the cookie may not be modified
 AT_SETUP([ofproto - no mod flow with cookie change (OpenFlow 1.2)])
 OVS_VSWITCHD_START
@@ -378,6 +415,28 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto - mod flows based on cookie mask (OpenFlow 1.1)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 cookie=0x1,in_port=1,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 cookie=0x1,in_port=2,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 cookie=0x2,in_port=3,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:1
+ cookie=0x1, in_port=2 actions=output:1
+ cookie=0x2, in_port=3 actions=output:1
+OFPST_FLOW reply (OF1.1):
+])
+
+AT_CHECK([ovs-ofctl -O OpenFlow11 mod-flows br0 cookie=0x1/0xff,actions=4])
+AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:4
+ cookie=0x1, in_port=2 actions=output:4
+ cookie=0x2, in_port=3 actions=output:1
+OFPST_FLOW reply (OF1.1):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto - mod flows based on cookie mask (OpenFlow 1.2)])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x1,in_port=1,actions=1])
@@ -423,7 +482,7 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto - mod flow with cookie miss (mask==0)])
+AT_SETUP([ofproto - mod flow with cookie miss (mask==0) - NXM])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl -F nxm mod-flows br0 in_port=1,actions=1])
 AT_CHECK([ovs-ofctl -F nxm dump-flows br0 | ofctl_strip | sort], [0], [dnl
@@ -433,7 +492,26 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto - mod flow with cookie miss (mask!=0)])
+AT_SETUP([ofproto - mod flow with cookie miss (mask==0) - OF1.1])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O openflow11 mod-flows br0 in_port=1,actions=1])
+AT_CHECK([ovs-ofctl -O openflow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ in_port=1 actions=output:1
+OFPST_FLOW reply (OF1.1):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - mod flow with cookie miss (mask==0) - OF1.2])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O openflow12 mod-flows br0 in_port=1,actions=1])
+AT_CHECK([ovs-ofctl -O openflow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+OFPST_FLOW reply (OF1.2):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - mod flow with cookie miss (mask!=0) - NXM])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl -F nxm mod-flows br0 cookie=1/1,in_port=1,actions=1])
 AT_CHECK([ovs-ofctl -F nxm dump-flows br0 | ofctl_strip | sort], [0], [dnl
@@ -442,6 +520,24 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto - mod flow with cookie miss (mask!=0) - OF1.1])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O openflow11 mod-flows br0 cookie=1/1,in_port=1,actions=1])
+AT_CHECK([ovs-ofctl -O openflow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+OFPST_FLOW reply (OF1.1):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - mod flow with cookie miss (mask!=0) - OF1.2])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O openflow12 mod-flows br0 cookie=1/1,in_port=1,actions=1])
+AT_CHECK([ovs-ofctl -O openflow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+OFPST_FLOW reply (OF1.2):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto - del flows with cookies])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1])
@@ -533,6 +629,38 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto - del flows based on table id (OpenFlow 1.1)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 cookie=0x1,in_port=1,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 cookie=0x2,in_port=2,table=1,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:1
+ cookie=0x2, table=1, in_port=2 actions=output:1
+OFPST_FLOW reply (OF1.1):
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 del-flows br0 table=0])
+AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x2, table=1, in_port=2 actions=output:1
+OFPST_FLOW reply (OF1.1):
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 del-flows br0 table=1])
+AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+OFPST_FLOW reply (OF1.1):
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 cookie=0x1,in_port=1,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow11 add-flow br0 cookie=0x2,in_port=2,table=1,actions=1])
+AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:1
+ cookie=0x2, table=1, in_port=2 actions=output:1
+OFPST_FLOW reply (OF1.1):
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 del-flows br0])
+AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+OFPST_FLOW reply (OF1.1):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto - del flows based on table id (OpenFlow 1.2)])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 cookie=0x1,in_port=1,actions=1])
@@ -1359,6 +1487,7 @@ dnl controllers despite the spec) as meaning a packet that was generated
 dnl by the controller.
 AT_SETUP([ofproto - packet-out from controller (OpenFlow 1.0)])
 OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1])
 
 # Start a monitor listening for packet-ins.
 AT_CHECK([ovs-ofctl -P openflow10 monitor br0 --detach --no-chdir --pidfile])
@@ -1368,13 +1497,15 @@ ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
 AT_CAPTURE_FILE([monitor.log])
 
 # Send some packet-outs with OFPP_NONE and OFPP_CONTROLLER (65533) as in_port.
-AT_CHECK([ovs-ofctl packet-out br0 none controller '0001020304050010203040501234'])
-AT_CHECK([ovs-ofctl packet-out br0 controller controller '0001020304050010203040505678'])
+AT_CHECK([ovs-ofctl packet-out br0 none controller,1 '0001020304050010203040501234'])
+AT_CHECK([ovs-ofctl packet-out br0 controller controller,1 '0001020304050010203040505678'])
 
 # Stop the monitor and check its output.
 ovs-appctl -t ovs-ofctl ofctl/barrier
 ovs-appctl -t ovs-ofctl exit
 
+ovs-ofctl dump-ports br0
+
 AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
 OFPT_PACKET_IN: total_len=14 in_port=ANY (via action) data_len=14 (unbuffered)
 metadata=0,in_port=0,vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
index fa2c3ff..4449f7a 100644 (file)
@@ -327,6 +327,8 @@ AT_CHECK([RUN_OVS_VSCTL([del-port a])], [1], [],
   [ovs-vsctl: cannot delete port a because it is the local port for bridge a (deleting this port requires deleting the entire bridge)
 ],
   [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL([--if-exists del-port a])], [0], [], [],
+  [OVS_VSCTL_CLEANUP])
 AT_CHECK([RUN_OVS_VSCTL([--may-exist add-port a b1])], [1], [],
   [ovs-vsctl: "--may-exist add-port a b1" but b1 is actually attached to bridge b
 ],
index 16a1d95..8a01f0a 100644 (file)
@@ -234,6 +234,13 @@ AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-db ordinals], [2],
   [], [Failed to find the database.
 ovs-appctl: ovsdb-server: server returned an error
 ])
+
+# Add a removed database.
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db2], [0])
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-dbs],
+  [0], [constraints
+])
+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])
index 0e7525c..41e2e38 100644 (file)
@@ -1,4 +1,4 @@
-/* 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.
@@ -71,9 +71,14 @@ parse_bundle_actions(char *actions)
     struct ofpact_bundle *bundle;
     struct ofpbuf ofpacts;
     struct ofpact *action;
+    char *error;
 
     ofpbuf_init(&ofpacts, 0);
-    bundle_parse_load(actions, &ofpacts);
+    error = bundle_parse_load(actions, &ofpacts);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
+
     action = ofpacts.data;
     bundle = ofpact_get_BUNDLE(xmemdup(action, action->len));
     ofpbuf_uninit(&ofpacts);
index 5f736d6..b08f236 100644 (file)
@@ -94,9 +94,12 @@ static void
 test_rfc1624(void)
 {
     /* "...an IP packet header in which a 16-bit field m = 0x5555..." */
-    uint8_t data[32] =
-        "\xfe\x8f\xc1\x14\x4b\x6f\x70\x2a\x80\x29\x78\xc0\x58\x81\x77\xaa"
-        "\x66\x64\xfc\x96\x63\x97\x64\xee\x12\x53\x1d\xa9\x2d\xa9\x55\x55";
+    uint8_t data[32] = {
+        0xfe, 0x8f, 0xc1, 0x14, 0x4b, 0x6f, 0x70, 0x2a,
+        0x80, 0x29, 0x78, 0xc0, 0x58, 0x81, 0x77, 0xaa,
+        0x66, 0x64, 0xfc, 0x96, 0x63, 0x97, 0x64, 0xee,
+        0x12, 0x53, 0x1d, 0xa9, 0x2d, 0xa9, 0x55, 0x55
+    };
 
     /* "...the one's complement sum of all other header octets is 0xCD7A." */
     assert(ntohs(csum(data, sizeof data - 2)) == 0xffff - 0xcd7a);
index 7a3ef72..f0a8b93 100644 (file)
@@ -178,6 +178,12 @@ mod2_hash(int value)
     return value % 2;
 }
 
+static size_t
+multipart_hash(int value)
+{
+    return (mod4_hash(value) << 16) | (constant_hash(value) & 0xFFFF);
+}
+
 /* Tests basic hindex insertion and deletion. */
 static void
 test_hindex_insert_delete(hash_func *hash)
@@ -298,6 +304,7 @@ run_test(void (*function)(hash_func *))
         mod4_hash,
         mod3_hash,
         mod2_hash,
+        multipart_hash,
     };
     size_t i;
 
index e8aacff..f1b12e2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2012 Nicira, Inc.
+ * Copyright (c) 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.
@@ -35,6 +35,7 @@ main(int argc, char *argv[])
     enum { MP_MAX_LINKS = 63 };
     struct ofpact_multipath mp;
     bool ok = true;
+    char *error;
     int n;
 
     set_program_name(argv[0]);
@@ -44,7 +45,11 @@ main(int argc, char *argv[])
         ovs_fatal(0, "usage: %s multipath_action", program_name);
     }
 
-    multipath_parse(&mp, argv[1]);
+    error = multipath_parse(&mp, argv[1]);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
+
     for (n = 1; n <= MP_MAX_LINKS; n++) {
         enum { N_FLOWS = 65536 };
         double disruption, perfect, distribution;
index 5afe2f7..6bf8ed5 100644 (file)
@@ -90,6 +90,69 @@ test_ctz(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     check_ctz(0, 32);
 }
 
+/* Returns a random number in the range 'min'...'max' inclusive. */
+static uint32_t
+random_in_range(uint32_t min, uint32_t max)
+{
+    return min == max ? min : min + random_range(max - min + 1);
+}
+
+static void
+check_rup2(uint32_t x, int n)
+{
+    uint32_t rup2 = ROUND_UP_POW2(x);
+    if (rup2 != n) {
+        fprintf(stderr, "ROUND_UP_POW2(%#"PRIx32") is %#"PRIx32" "
+                "but should be %#"PRIx32"\n", x, rup2, n);
+        abort();
+    }
+}
+
+static void
+test_round_up_pow2(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    int n;
+
+    for (n = 0; n < 32; n++) {
+        /* Min, max value for which ROUND_UP_POW2 should yield (1 << n). */
+        uint32_t min = ((1u << n) >> 1) + 1;
+        uint32_t max = 1u << n;
+
+        check_rup2(min, 1u << n);
+        check_rup2(max, 1u << n);
+        check_rup2(random_in_range(min, max), 1u << n);
+    }
+    check_rup2(0, 0);
+}
+
+static void
+check_rdp2(uint32_t x, int n)
+{
+    uint32_t rdp2 = ROUND_DOWN_POW2(x);
+    if (rdp2 != n) {
+        fprintf(stderr, "ROUND_DOWN_POW2(%#"PRIx32") is %#"PRIx32" "
+                "but should be %#"PRIx32"\n", x, rdp2, n);
+        abort();
+    }
+}
+
+static void
+test_round_down_pow2(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    int n;
+
+    for (n = 0; n < 32; n++) {
+        /* Min, max value for which ROUND_DOWN_POW2 should yield (1 << n). */
+        uint32_t min = 1u << n;
+        uint32_t max = ((1u << n) << 1) - 1;
+
+        check_rdp2(min, 1u << n);
+        check_rdp2(max, 1u << n);
+        check_rdp2(random_in_range(min, max), 1u << n);
+    }
+    check_rdp2(0, 0);
+}
+
 static void
 shuffle(unsigned int *p, size_t n)
 {
@@ -346,6 +409,8 @@ test_assert(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 \f
 static const struct command commands[] = {
     {"ctz", 0, 0, test_ctz},
+    {"round_up_pow2", 0, 0, test_round_up_pow2},
+    {"round_down_pow2", 0, 0, test_round_down_pow2},
     {"popcount", 0, 0, test_popcount},
     {"log_2_floor", 0, 0, test_log_2_floor},
     {"bitwise_copy", 0, 0, test_bitwise_copy},
index 797748c..9f2bb63 100644 (file)
@@ -23,6 +23,7 @@ scripts_DATA += utilities/ovs-lib
 EXTRA_DIST += \
        utilities/ovs-check-dead-ifs.in \
        utilities/ovs-ctl.in \
+       utilities/ovs-dev.py \
        utilities/ovs-l3ping.in \
        utilities/ovs-lib.in \
        utilities/ovs-parse-backtrace.in \
index 9d1ff66..528b720 100644 (file)
@@ -14,9 +14,11 @@ bugtool_plugins = \
        utilities/bugtool/plugins/system-configuration/openvswitch.xml
 
 bugtool_scripts = \
+       utilities/bugtool/ovs-bugtool-bfd-show \
        utilities/bugtool/ovs-bugtool-cfm-show \
        utilities/bugtool/ovs-bugtool-coverage-show \
        utilities/bugtool/ovs-bugtool-lacp-show \
+       utilities/bugtool/ovs-bugtool-list-dbs \
        utilities/bugtool/ovs-bugtool-memory-show \
        utilities/bugtool/ovs-bugtool-tc-class-show \
        utilities/bugtool/ovs-bugtool-vsctl-show \
diff --git a/utilities/bugtool/ovs-bugtool-bfd-show b/utilities/bugtool/ovs-bugtool-bfd-show
new file mode 100755 (executable)
index 0000000..0173595
--- /dev/null
@@ -0,0 +1,19 @@
+#! /bin/sh
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General
+# Public License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+#
+# Copyright (C) 2013 Nicira, Inc.
+
+ovs-appctl bfd/show
diff --git a/utilities/bugtool/ovs-bugtool-list-dbs b/utilities/bugtool/ovs-bugtool-list-dbs
new file mode 100755 (executable)
index 0000000..28775fa
--- /dev/null
@@ -0,0 +1,19 @@
+#! /bin/sh
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General
+# Public License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+#
+# Copyright (C) 2013 Nicira, Inc.
+
+ovs-appctl -t ovsdb-server ovsdb-server/list-dbs
index d79b4f3..00247f9 100644 (file)
   <command label="dump-ovsdb" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-ovsdb-dump</command>
   <command label="ovs-appctl-lacp-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-lacp-show</command>
   <command label="ovs-appctl-cfm-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-cfm-show</command>
+  <command label="ovs-appctl-bfd-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-bfd-show</command>
   <command label="ovs-appctl-coverage-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-coverage-show</command>
   <command label="ovs-appctl-bond-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-bond-show</command>
   <command label="ovs-appctl-memory-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-memory-show</command>
   <command label="ovs-ofctl-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-ovs-ofctl-show</command>
   <command label="ovs-ofctl-dump-flows" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-ovs-ofctl-dump-flows</command>
   <command label="ovs-appctl-dpif" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-ovs-appctl-dpif</command>
+  <command label="ovs-appctl-list-dbs" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-list-dbs</command>
 </collect>
index 6d08af5..d9ae983 100644 (file)
@@ -278,6 +278,7 @@ parse_options(int argc, char *argv[])
 
     for (;;) {
         int indexptr;
+        char *error;
         int c;
 
         c = getopt_long(argc, argv, short_options, long_options, &indexptr);
@@ -327,8 +328,11 @@ parse_options(int argc, char *argv[])
             break;
 
         case OPT_WITH_FLOWS:
-            parse_ofp_flow_mod_file(optarg, OFPFC_ADD, &default_flows,
-                                    &n_default_flows);
+            error = parse_ofp_flow_mod_file(optarg, OFPFC_ADD, &default_flows,
+                                            &n_default_flows);
+            if (error) {
+                ovs_fatal(0, "%s", error);
+            }
             break;
 
         case OPT_UNIXCTL:
index 6465fdf..0735160 100755 (executable)
@@ -224,10 +224,10 @@ start_forwarding () {
         log_success_msg "ovs-vswitchd is already running"
     else
         # Increase the limit on the number of open file descriptors.
-        # On Linux, ovs-vswitchd needs about one file descriptor per
-        # switch port, so this allows a very large number of switch
-        # ports.
-        ulimit -n 5000
+        # On Linux, ovs-vswitchd needs about three file descriptors
+        # per bridge and one file descriptor per bridge port, so this
+        # allows a very large number of bridges and ports.
+        ulimit -n 7500
 
            # Start ovs-vswitchd.
            set ovs-vswitchd unix:"$DB_SOCK"
diff --git a/utilities/ovs-dev.py b/utilities/ovs-dev.py
new file mode 100755 (executable)
index 0000000..8c8f5bd
--- /dev/null
@@ -0,0 +1,300 @@
+#!/usr/bin/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.
+
+import optparse
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+
+ENV = os.environ
+HOME = ENV["HOME"]
+OVS_SRC = HOME + "/ovs"
+ROOT = HOME + "/root"
+PATH = "%(ovs)s/utilities:%(ovs)s/ovsdb:%(ovs)s/vswitchd" % {"ovs": OVS_SRC}
+
+ENV["CFLAGS"] = "-g -O0 -Wall -Wextra -Wno-deprecated-declarations"
+ENV["PATH"] = PATH + ":" + ENV["PATH"]
+
+options = None
+parser = None
+commands = []
+
+
+def _sh(*args, **kwargs):
+    print "------> " + " ".join(args)
+    shell = len(args) == 1
+    if kwargs.get("capture", False):
+        proc = subprocess.Popen(args, stdout=subprocess.PIPE, shell=shell)
+        return proc.stdout.readlines()
+    elif kwargs.get("check", True):
+        subprocess.check_call(args, shell=shell)
+    else:
+        subprocess.call(args, shell=shell)
+
+
+def uname():
+    return _sh("uname", "-r", capture=True)[0].strip()
+
+
+def conf():
+    tag()
+    configure = ["./configure", "--prefix=" + ROOT, "--localstatedir=" + ROOT,
+                 "--with-logdir=%s/log" % ROOT, "--with-rundir=%s/run" % ROOT,
+                 "--with-linux=/lib/modules/%s/build" % uname(),
+                 "--with-dbdir=" + ROOT]
+
+    if options.werror:
+        configure.append("--enable-Werror")
+
+    if options.cache_time:
+        configure.append("--enable-cache-time")
+
+    if options.mandir:
+        configure.append("--mandir=" + options.mandir)
+
+    _sh("./boot.sh")
+    _sh(*configure)
+commands.append(conf)
+
+
+def make(args=""):
+    make = "make -s -j 8 " + args
+    try:
+        _sh("cgcc", "--version", capture=True)
+        make += " C=1"
+    except OSError:
+        pass
+    _sh(make)
+commands.append(make)
+
+
+def check():
+    make("check")
+commands.append(check)
+
+
+def tag():
+    ctags = ['ctags', '-R', '-f', '.tags']
+
+    try:
+        _sh(*(ctags + ['--exclude="datapath/"']))
+    except:
+        try:
+            _sh(*ctags)  # Some versions of ctags don't have --exclude
+        except:
+            pass
+
+    try:
+        _sh('cscope', '-R', '-b')
+    except:
+        pass
+commands.append(tag)
+
+
+def kill():
+    for proc in ["ovs-vswitchd", "ovsdb-server"]:
+        if os.path.exists("%s/run/openvswitch/%s.pid" % (ROOT, proc)):
+            _sh("ovs-appctl", "-t", proc, "exit", check=False)
+            time.sleep(.1)
+        _sh("sudo", "killall", "-q", "-2", proc, check=False)
+commands.append(kill)
+
+
+def reset():
+    kill()
+    if os.path.exists(ROOT):
+        shutil.rmtree(ROOT)
+    for dp in _sh("ovs-dpctl dump-dps", capture=True):
+        _sh("ovs-dpctl", "del-dp", dp.strip())
+commands.append(reset)
+
+
+def run():
+    kill()
+    for d in ["log", "run"]:
+        d = "%s/%s" % (ROOT, d)
+        shutil.rmtree(d, ignore_errors=True)
+        os.makedirs(d)
+
+    pki_dir = ROOT + "/pki"
+    if not os.path.exists(pki_dir):
+        os.mkdir(pki_dir)
+        os.chdir(pki_dir)
+        _sh("ovs-pki init")
+        _sh("ovs-pki req+sign ovsclient")
+        os.chdir(OVS_SRC)
+
+    if not os.path.exists(ROOT + "/conf.db"):
+        _sh("ovsdb-tool", "create", ROOT + "/conf.db",
+            OVS_SRC + "/vswitchd/vswitch.ovsschema")
+
+    opts = ["--pidfile", "--log-file", "--enable-dummy"]
+
+    _sh(*(["ovsdb-server",
+           "--remote=punix:%s/run/db.sock" % ROOT,
+           "--remote=db:Open_vSwitch,Open_vSwitch,manager_options",
+           "--private-key=db:Open_vSwitch,SSL,private_key",
+           "--certificate=db:Open_vSwitch,SSL,certificate",
+           "--bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert",
+           "--detach", "-vconsole:off"] + opts))
+
+    _sh("ovs-vsctl --no-wait --bootstrap set-ssl %s/ovsclient-privkey.pem" \
+        " %s/ovsclient-cert.pem %s/vswitchd.cacert"
+        % (pki_dir, pki_dir, pki_dir))
+    version = _sh("ovs-vsctl --no-wait --version", capture=True)
+    version = version[0].strip().split()[3]
+    root_uuid = _sh("ovs-vsctl --no-wait --bare list Open_vSwitch",
+                    capture=True)[0].strip()
+    _sh("ovs-vsctl --no-wait set Open_vSwitch %s ovs_version=%s"
+        % (root_uuid, version))
+
+    cmd = [OVS_SRC + "/vswitchd/ovs-vswitchd"]
+    if options.gdb:
+        cmd = ["gdb", "--args"] + cmd
+    elif options.valgrind:
+        cmd = ["valgrind", "--track-origins=yes"] + cmd
+    else:
+        cmd = ["sudo"] + cmd
+        opts = opts + ["-vconsole:off", "--detach"]
+    _sh(*(cmd + opts))
+commands.append(run)
+
+
+def modinst():
+    if not os.path.exists("/lib/modules"):
+        print "Missing modules directory.  Is this a Linux system?"
+        sys.exit(1)
+
+    try:
+        _sh("rmmod", "openvswitch")
+    except subprocess.CalledProcessError, e:
+        pass  # Module isn't loaded
+
+    try:
+        _sh("rm /lib/modules/%s/extra/openvswitch.ko" % uname())
+    except subprocess.CalledProcessError, e:
+        pass  # Module isn't installed
+
+    conf()
+    make()
+    make("modules_install")
+
+    _sh("modprobe", "openvswitch")
+    _sh("dmesg | grep openvswitch | tail -1")
+commands.append(modinst)
+
+
+def env():
+    print "export PATH=" + ENV["PATH"]
+commands.append(env)
+
+
+def doc():
+    parser.print_help()
+    print \
+"""
+This program is designed to help developers build and run Open vSwitch without
+necessarily needing to know the gory details. Given some basic requirements
+(described below), it can be used to build and run Open vSwitch, keeping
+runtime files in the user's home directory.
+
+Basic Configuration:
+    # This section can be run as a script on ubuntu systems.
+
+    # First install the basic requirements needed to build Open vSwitch.
+    sudo apt-get install git build-essential libtool autoconf pkg-config \\
+            libssl-dev pkg-config gdb linux-headers-`uname -r`
+
+    # Next clone the Open vSwitch source.
+    git clone git://git.openvswitch.org/openvswitch %(ovs)s
+
+    # Setup environment variables.
+    `%(v)s env`
+
+    # Build the switch.
+    %(v)s conf make
+
+    # Install the kernel module
+    sudo insmod %(ovs)s/datapath/linux/openvswitch.ko
+
+    # Run the switch.
+    %(v)s run
+
+Commands:
+    conf    - Configure the ovs source.
+    make    - Build the source (must have been configured).
+    check   - Run the unit tests.
+    tag     - Run ctags and cscope over the source.
+    kill    - Kill all running instances of ovs.
+    reset   - Reset any runtime configuration in %(run)s.
+    run     - Run ovs.
+    modinst - Build ovs and install the kernel module.
+    env     - Print the required path environment variable.
+    doc     - Print this message.
+""" % {"ovs": OVS_SRC, "v": sys.argv[0], "run": ROOT}
+    sys.exit(0)
+commands.append(doc)
+
+
+def main():
+    global options
+    global parser
+
+    description = "Open vSwitch developer configuration. Try `%prog doc`."
+    cmd_names = [c.__name__ for c in commands]
+    parser = optparse.OptionParser(usage="usage: %prog"
+                                   + " [options] [%s] ..."
+                                   % "|".join(cmd_names),
+                                   description=description)
+
+    group = optparse.OptionGroup(parser, "conf")
+    group.add_option("--disable-Werror", dest="werror", action="store_false",
+                     default=True, help="compile without the Werror flag")
+    group.add_option("--cache-time", dest="cache_time",
+                     action="store_true", help="configure with cached timing")
+    group.add_option("--mandir", dest="mandir", metavar="MANDIR",
+                     help="configure the man documentation install directory")
+    parser.add_option_group(group)
+
+    group = optparse.OptionGroup(parser, "run")
+    group.add_option("-g", "--gdb", dest="gdb", action="store_true",
+                     help="run ovs-vswitchd under gdb")
+    group.add_option("--valgrind", dest="valgrind", action="store_true",
+                     help="run ovs-vswitchd under valgrind")
+    parser.add_option_group(group)
+
+    options, args = parser.parse_args()
+
+    for arg in args:
+        if arg not in cmd_names:
+            print "Unknown argument " + arg
+            doc()
+
+    try:
+        os.chdir(OVS_SRC)
+    except OSError:
+        print "Missing %s." % OVS_SRC
+        doc()
+
+    for arg in args:
+        for cmd in commands:
+            if arg == cmd.__name__:
+                cmd()
+
+
+if __name__ == '__main__':
+    main()
index adf86ad..4d5a84e 100644 (file)
@@ -866,8 +866,14 @@ prepare_dump_flows(int argc, char *argv[], bool aggregate,
     enum ofputil_protocol usable_protocols, protocol;
     struct ofputil_flow_stats_request fsr;
     struct vconn *vconn;
+    char *error;
+
+    error = parse_ofp_flow_stats_request_str(&fsr, aggregate,
+                                             argc > 2 ? argv[2] : "");
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
 
-    parse_ofp_flow_stats_request_str(&fsr, aggregate, argc > 2 ? argv[2] : "");
     usable_protocols = ofputil_flow_stats_request_usable_protocols(&fsr);
 
     protocol = open_vconn(argv[1], &vconn);
@@ -1087,8 +1093,12 @@ ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
 {
     struct ofputil_flow_mod *fms = NULL;
     size_t n_fms = 0;
+    char *error;
 
-    parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms);
+    error = parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
     ofctl_flow_mod__(argv[1], fms, n_fms);
     free(fms);
 }
@@ -1100,7 +1110,12 @@ ofctl_flow_mod(int argc, char *argv[], uint16_t command)
         ofctl_flow_mod_file(argc, argv, command);
     } else {
         struct ofputil_flow_mod fm;
-        parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command, false);
+        char *error;
+
+        error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command);
+        if (error) {
+            ovs_fatal(0, "%s", error);
+        }
         ofctl_flow_mod__(argv[1], &fm, 1);
     }
 }
@@ -1439,8 +1454,12 @@ ofctl_monitor(int argc, char *argv[])
         } else if (!strncmp(arg, "watch:", 6)) {
             struct ofputil_flow_monitor_request fmr;
             struct ofpbuf *msg;
+            char *error;
 
-            parse_flow_monitor_request(&fmr, arg + 6);
+            error = parse_flow_monitor_request(&fmr, arg + 6);
+            if (error) {
+                ovs_fatal(0, "%s", error);
+            }
 
             msg = ofpbuf_new(0);
             ofputil_append_flow_monitor_request(&fmr, msg);
@@ -1538,10 +1557,14 @@ ofctl_packet_out(int argc, char *argv[])
     struct ofputil_packet_out po;
     struct ofpbuf ofpacts;
     struct vconn *vconn;
+    char *error;
     int i;
 
     ofpbuf_init(&ofpacts, 64);
-    parse_ofpacts(argv[3], &ofpacts);
+    error = parse_ofpacts(argv[3], &ofpacts);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
 
     po.buffer_id = UINT32_MAX;
     po.in_port = str_to_port_no(argv[1], argv[2]);
@@ -1905,6 +1928,7 @@ static enum ofputil_protocol
 read_flows_from_file(const char *filename, struct classifier *cls, int index)
 {
     enum ofputil_protocol usable_protocols;
+    int line_number;
     struct ds s;
     FILE *file;
 
@@ -1915,11 +1939,16 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index)
 
     ds_init(&s);
     usable_protocols = OFPUTIL_P_ANY;
-    while (!ds_get_preprocessed_line(&s, file)) {
+    line_number = 0;
+    while (!ds_get_preprocessed_line(&s, file, &line_number)) {
         struct fte_version *version;
         struct ofputil_flow_mod fm;
+        char *error;
 
-        parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), true);
+        error = parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s));
+        if (error) {
+            ovs_fatal(0, "%s:%d: %s", filename, line_number, error);
+        }
 
         version = xmalloc(sizeof *version);
         version->cookie = fm.new_cookie;
@@ -2050,6 +2079,7 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
     fm.cookie = htonll(0);
     fm.cookie_mask = htonll(0);
     fm.new_cookie = version->cookie;
+    fm.modify_cookie = true;
     fm.table_id = 0xff;
     fm.command = command;
     fm.idle_timeout = version->idle_timeout;
@@ -2232,8 +2262,12 @@ static void
 ofctl_parse_flow(int argc OVS_UNUSED, char *argv[])
 {
     struct ofputil_flow_mod fm;
+    char *error;
 
-    parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD, false);
+    error = parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
     ofctl_parse_flows__(&fm, 1);
 }
 
@@ -2244,8 +2278,12 @@ ofctl_parse_flows(int argc OVS_UNUSED, char *argv[])
 {
     struct ofputil_flow_mod *fms = NULL;
     size_t n_fms = 0;
+    char *error;
 
-    parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms);
+    error = parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
     ofctl_parse_flows__(fms, n_fms);
     free(fms);
 }
@@ -2367,7 +2405,7 @@ ofctl_parse_ofp10_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     struct ds in;
 
     ds_init(&in);
-    while (!ds_get_preprocessed_line(&in, stdin)) {
+    while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
         struct ofpbuf of10_out;
         struct ofpbuf of10_in;
         struct ofpbuf ofpacts;
@@ -2431,7 +2469,7 @@ ofctl_parse_ofp10_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 
     ds_init(&in);
     ds_init(&expout);
-    while (!ds_get_preprocessed_line(&in, stdin)) {
+    while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
         struct ofpbuf match_in, match_expout;
         struct ofp10_match match_out;
         struct ofp10_match match_normal;
@@ -2503,7 +2541,7 @@ ofctl_parse_ofp11_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     struct ds in;
 
     ds_init(&in);
-    while (!ds_get_preprocessed_line(&in, stdin)) {
+    while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
         struct ofpbuf match_in;
         struct ofp11_match match_out;
         struct match match;
@@ -2552,7 +2590,7 @@ ofctl_parse_ofp11_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     struct ds in;
 
     ds_init(&in);
-    while (!ds_get_preprocessed_line(&in, stdin)) {
+    while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
         struct ofpbuf of11_out;
         struct ofpbuf of11_in;
         struct ofpbuf ofpacts;
@@ -2610,7 +2648,7 @@ ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     struct ds in;
 
     ds_init(&in);
-    while (!ds_get_preprocessed_line(&in, stdin)) {
+    while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
         struct ofpbuf of11_out;
         struct ofpbuf of11_in;
         struct ofpbuf ofpacts;
@@ -2699,6 +2737,7 @@ ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
     struct match of11_match;
 
     enum ofperr error;
+    char *error_s;
 
     match_init_catchall(&match);
     match.flow.vlan_tci = htons(strtoul(argv[1], NULL, 16));
@@ -2708,7 +2747,10 @@ ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
     string_s = match_to_string(&match, OFP_DEFAULT_PRIORITY);
     printf("%s -> ", string_s);
     fflush(stdout);
-    parse_ofp_str(&fm, -1, string_s, false);
+    error_s = parse_ofp_str(&fm, -1, string_s);
+    if (error_s) {
+        ovs_fatal(0, "%s", error_s);
+    }
     printf("%04"PRIx16"/%04"PRIx16"\n",
            ntohs(fm.match.flow.vlan_tci),
            ntohs(fm.match.wc.masks.vlan_tci));
index 2d8c7c7..e679e0d 100644 (file)
@@ -2021,9 +2021,12 @@ cmd_del_port(struct vsctl_context *ctx)
 
     vsctl_context_populate_cache(ctx);
     if (find_bridge(ctx, target, false)) {
-        vsctl_fatal("cannot delete port %s because it is the local port "
-                    "for bridge %s (deleting this port requires deleting "
-                    "the entire bridge)", target, target);
+        if (must_exist) {
+            vsctl_fatal("cannot delete port %s because it is the local port "
+                        "for bridge %s (deleting this port requires deleting "
+                        "the entire bridge)", target, target);
+        }
+        port = NULL;
     } else if (!with_iface) {
         port = find_port(ctx, target, must_exist);
     } else {
index 28bf082..a73379c 100644 (file)
@@ -18,6 +18,7 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <stdlib.h>
+#include "async-append.h"
 #include "bfd.h"
 #include "bitmap.h"
 #include "bond.h"
@@ -367,6 +368,7 @@ bridge_init(const char *remote)
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_link_state);
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_link_resets);
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_mac_in_use);
+    ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_ifindex);
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_mtu);
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_ofport);
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_statistics);
@@ -1810,6 +1812,7 @@ iface_refresh_status(struct iface *iface)
     int mtu;
     int64_t mtu_64;
     uint8_t mac[ETH_ADDR_LEN];
+    int64_t ifindex64;
     int error;
 
     if (iface_is_synthetic(iface)) {
@@ -1855,6 +1858,14 @@ iface_refresh_status(struct iface *iface)
     } else {
         ovsrec_interface_set_mac_in_use(iface->cfg, NULL);
     }
+
+    /* The netdev may return a negative number (such as -EOPNOTSUPP)
+     * if there is no valid ifindex number. */
+    ifindex64 = netdev_get_ifindex(iface->netdev);
+    if (ifindex64 < 0) {
+        ifindex64 = 0;
+    }
+    ovsrec_interface_set_ifindex(iface->cfg, &ifindex64, 1);
 }
 
 /* Writes 'iface''s CFM statistics to the database. 'iface' must not be
@@ -2434,6 +2445,8 @@ bridge_run(void)
              * process that forked us to exit successfully. */
             daemonize_complete();
 
+            async_append_enable();
+
             VLOG_INFO_ONCE("%s (Open vSwitch) %s", program_name, VERSION);
         }
     }
@@ -3573,6 +3586,7 @@ iface_clear_db_record(const struct ovsrec_interface *if_cfg)
         ovsrec_interface_set_cfm_remote_mpids(if_cfg, NULL, 0);
         ovsrec_interface_set_lacp_current(if_cfg, NULL, 0);
         ovsrec_interface_set_statistics(if_cfg, NULL, NULL, 0);
+        ovsrec_interface_set_ifindex(if_cfg, NULL, 0);
     }
 }
 
index 86500c9..3eb87a6 100644 (file)
@@ -208,7 +208,6 @@ enabled.
 .so lib/vlog-unixctl.man
 .so lib/memory-unixctl.man
 .so lib/coverage-unixctl.man
-.so lib/stress-unixctl.man
 .
 .SH "OPENFLOW IMPLEMENTATION"
 .
@@ -237,13 +236,12 @@ We believe these limits to be accurate as of this writing.  These
 limits assume the use of the Linux kernel datapath.
 .
 .IP \(bu
-Approximately 256 bridges given the allowance of 5,000 file
-descriptors that \fBovs\-ctl\fR(8) configures.  (\fBovs\-vswitchd\fR
-requires 17 file descriptors per datapath.)
-.
-.IP \(bu
-65,280 ports per bridge.  Performance will degrade beyond 1,024 ports
-per bridge due to fixed hash table sizing.
+\fBovs\-vswitchd\fR started through \fBovs\-ctl\fR(8) provides a limit of 7500
+file descriptors.  The limits on the number of bridges and ports is decided by
+the availability of file descriptors.  With the Linux kernel datapath, creation
+of a single bridge consumes 3 file descriptors and adding a port consumes
+1 file descriptor.  Performance will degrade beyond 1,024 ports per bridge due
+to fixed hash table sizing.  Other platforms may have different limitations.
 .
 .IP \(bu
 2,048 MAC learning entries per bridge, by default.  (This is
index bb087bd..bc45dac 100644 (file)
@@ -42,7 +42,6 @@
 #include "simap.h"
 #include "stream-ssl.h"
 #include "stream.h"
-#include "stress.h"
 #include "svec.h"
 #include "timeval.h"
 #include "unixctl.h"
@@ -50,7 +49,6 @@
 #include "vconn.h"
 #include "vlog.h"
 #include "lib/vswitch-idl.h"
-#include "worker.h"
 
 VLOG_DEFINE_THIS_MODULE(vswitchd);
 
@@ -75,7 +73,6 @@ main(int argc, char *argv[])
 
     proctitle_init(argc, argv);
     set_program_name(argv[0]);
-    stress_init_command();
     remote = parse_options(argc, argv, &unixctl_path);
     signal(SIGPIPE, SIG_IGN);
     sighup = signal_register(SIGHUP);
@@ -94,8 +91,6 @@ main(int argc, char *argv[])
 #endif
     }
 
-    worker_start();
-
     retval = unixctl_server_create(unixctl_path, &unixctl);
     if (retval) {
         exit(EXIT_FAILURE);
@@ -107,7 +102,6 @@ main(int argc, char *argv[])
 
     exiting = false;
     while (!exiting) {
-        worker_run();
         if (signal_poll(sighup)) {
             vlog_reopen_log_file();
         }
@@ -126,7 +120,6 @@ main(int argc, char *argv[])
         unixctl_server_run(unixctl);
         netdev_run();
 
-        worker_wait();
         signal_wait(sighup);
         memory_wait();
         bridge_wait();
index f0f53c0..b164228 100644 (file)
 #include "dirs.h"
 #include "dynamic-string.h"
 #include "json.h"
+#include "latch.h"
 #include "ofpbuf.h"
+#include "ovs-thread.h"
 #include "poll-loop.h"
 #include "shash.h"
 #include "smap.h"
 #include "timeval.h"
 #include "vlog.h"
-#include "worker.h"
 
 VLOG_DEFINE_THIS_MODULE(system_stats);
 
@@ -505,46 +506,33 @@ get_filesys_stats(struct smap *stats OVS_UNUSED)
 \f
 #define SYSTEM_STATS_INTERVAL (5 * 1000) /* In milliseconds. */
 
-/* Whether the client wants us to report system stats. */
+static pthread_mutex_t mutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER;
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+static struct latch latch;
 static bool enabled;
+static bool started;
+static struct smap *system_stats;
 
-static enum {
-    S_DISABLED,                 /* Not enabled, nothing going on. */
-    S_WAITING,                  /* Sleeping for SYSTEM_STATS_INTERVAL ms. */
-    S_REQUEST_SENT,             /* Sent a request to worker. */
-    S_REPLY_RECEIVED            /* Received a reply from worker. */
-} state;
+static void *system_stats_thread_func(void *);
+static void discard_stats(void);
 
-/* In S_WAITING state: the next time to wake up.
- * In other states: not meaningful. */
-static long long int next_refresh;
-
-/* In S_REPLY_RECEIVED: the stats that have just been received.
- * In other states: not meaningful. */
-static struct smap *received_stats;
-
-static worker_request_func system_stats_request_cb;
-static worker_reply_func system_stats_reply_cb;
-
-/* Enables or disables system stats collection, according to 'new_enable'.
- *
- * Even if system stats are disabled, the caller should still periodically call
- * system_stats_run(). */
+/* Enables or disables system stats collection, according to 'enable'. */
 void
-system_stats_enable(bool new_enable)
+system_stats_enable(bool enable)
 {
-    if (new_enable != enabled) {
-        if (new_enable) {
-            if (state == S_DISABLED) {
-                state = S_WAITING;
-                next_refresh = time_msec();
-            }
-        } else {
-            if (state == S_WAITING) {
-                state = S_DISABLED;
+    if (enabled != enable) {
+        xpthread_mutex_lock(&mutex);
+        if (enable) {
+            if (!started) {
+                xpthread_create(NULL, NULL, system_stats_thread_func, NULL);
+                latch_init(&latch);
+                started = true;
             }
+            discard_stats();
+            xpthread_cond_signal(&cond);
         }
-        enabled = new_enable;
+        enabled = enable;
+        xpthread_mutex_unlock(&mutex);
     }
 }
 
@@ -553,41 +541,28 @@ system_stats_enable(bool new_enable)
  *
  * When a new snapshot is available (which only occurs if system stats are
  * enabled), returns it as an smap owned by the caller.  The caller must use
- * both smap_destroy() and free() to complete free the returned data.
+ * both smap_destroy() and free() to completely free the returned data.
  *
  * When no new snapshot is available, returns NULL. */
 struct smap *
 system_stats_run(void)
 {
-    switch (state) {
-    case S_DISABLED:
-        break;
-
-    case S_WAITING:
-        if (time_msec() >= next_refresh) {
-            worker_request(NULL, 0, NULL, 0, system_stats_request_cb,
-                           system_stats_reply_cb, NULL);
-            state = S_REQUEST_SENT;
-        }
-        break;
+    struct smap *stats = NULL;
 
-    case S_REQUEST_SENT:
-        break;
+    xpthread_mutex_lock(&mutex);
+    if (system_stats) {
+        latch_poll(&latch);
 
-    case S_REPLY_RECEIVED:
         if (enabled) {
-            state = S_WAITING;
-            next_refresh = time_msec() + SYSTEM_STATS_INTERVAL;
-            return received_stats;
+            stats = system_stats;
+            system_stats = NULL;
         } else {
-            smap_destroy(received_stats);
-            free(received_stats);
-            state = S_DISABLED;
+            discard_stats();
         }
-        break;
     }
+    xpthread_mutex_unlock(&mutex);
 
-    return NULL;
+    return stats;
 }
 
 /* Causes poll_block() to wake up when system_stats_run() needs to be
@@ -595,62 +570,54 @@ system_stats_run(void)
 void
 system_stats_wait(void)
 {
-    switch (state) {
-    case S_DISABLED:
-        break;
-
-    case S_WAITING:
-        poll_timer_wait_until(next_refresh);
-        break;
-
-    case S_REQUEST_SENT:
-        /* Someone else should be calling worker_wait() to wake up when the
-         * reply arrives, otherwise there's a bug. */
-        break;
-
-    case S_REPLY_RECEIVED:
-        poll_immediate_wake();
-        break;
+    if (enabled) {
+        latch_wait(&latch);
     }
 }
 
 static void
-system_stats_request_cb(struct ofpbuf *request OVS_UNUSED,
-                        const int fds[] OVS_UNUSED, size_t n_fds OVS_UNUSED)
+discard_stats(void)
 {
-    struct smap stats;
-    struct json *json;
-    char *s;
-
-    smap_init(&stats);
-    get_cpu_cores(&stats);
-    get_load_average(&stats);
-    get_memory_stats(&stats);
-    get_process_stats(&stats);
-    get_filesys_stats(&stats);
-
-    json = smap_to_json(&stats);
-    s = json_to_string(json, 0);
-    worker_reply(s, strlen(s) + 1, NULL, 0);
-
-    free(s);
-    json_destroy(json);
-    smap_destroy(&stats);
+    if (system_stats) {
+        smap_destroy(system_stats);
+        free(system_stats);
+        system_stats = NULL;
+    }
 }
 
-static void
-system_stats_reply_cb(struct ofpbuf *reply,
-                      const int fds[] OVS_UNUSED, size_t n_fds OVS_UNUSED,
-                      void *aux OVS_UNUSED)
+static void * NO_RETURN
+system_stats_thread_func(void *arg OVS_UNUSED)
 {
-    struct json *json = json_from_string(reply->data);
+    pthread_detach(pthread_self());
 
-    received_stats = xmalloc(sizeof *received_stats);
-    smap_init(received_stats);
-    smap_from_json(received_stats, json);
+    for (;;) {
+        long long int next_refresh;
+        struct smap *stats;
 
-    ovs_assert(state == S_REQUEST_SENT);
-    state = S_REPLY_RECEIVED;
-
-    json_destroy(json);
+        xpthread_mutex_lock(&mutex);
+        while (!enabled) {
+            xpthread_cond_wait(&cond, &mutex);
+        }
+        xpthread_mutex_unlock(&mutex);
+
+        stats = xmalloc(sizeof *stats);
+        smap_init(stats);
+        get_cpu_cores(stats);
+        get_load_average(stats);
+        get_memory_stats(stats);
+        get_process_stats(stats);
+        get_filesys_stats(stats);
+
+        xpthread_mutex_lock(&mutex);
+        discard_stats();
+        system_stats = stats;
+        latch_set(&latch);
+        xpthread_mutex_unlock(&mutex);
+
+        next_refresh = time_msec() + SYSTEM_STATS_INTERVAL;
+        do {
+            poll_timer_wait_until(next_refresh);
+            poll_block();
+        } while (time_msec() < next_refresh);
+    }
 }
index bb3ca48..c1c3ef4 100644 (file)
@@ -1,6 +1,6 @@
 {"name": "Open_vSwitch",
- "version": "7.2.0",
- "cksum": "543912409 19436",
+ "version": "7.3.0",
+ "cksum": "1081379034 19765",
  "tables": {
    "Open_vSwitch": {
      "columns": {
                   "min": 0, "max": "unlimited"}},
        "protocols": {
          "type": {"key": {"type": "string",
-           "enum": ["set", ["OpenFlow10", "OpenFlow12", "OpenFlow13"]]},
+           "enum": ["set", ["OpenFlow10",
+                            "OpenFlow11",
+                            "OpenFlow12",
+                            "OpenFlow13"]]},
           "min": 0, "max": "unlimited"}},
        "fail_mode": {
          "type": {"key": {"type": "string",
        "mac": {
          "type": {"key": {"type": "string"},
                   "min": 0, "max": 1}},
+       "ifindex": {
+         "type": {
+           "key": {"type": "integer",
+                   "minInteger": 0,
+                   "maxInteger": 4294967295},
+           "min": 0,
+           "max": 1},
+         "ephemeral": true},
        "external_ids": {
          "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}},
        "ofport": {
index 12780d6..957b02c 100644 (file)
         on a host.
       </column>
 
+      <column name="ifindex">
+        A positive interface index as defined for SNMP MIB-II in RFCs 1213 and
+        2863, if the interface has one, otherwise 0.  The ifindex is useful for
+        seamless integration with protocols such as SNMP and sFlow.
+      </column>
+
       <column name="mac_in_use">
         The MAC address in use by this interface.
       </column>
             times the expected reception rate, will signal a connectivity
             fault.  In the case of a unidirectional connectivity issue, the
             system not receiving BFD control messages will signal the problem
-            to its peer in the messages is transmists.
+            to its peer in the messages it transmits.
         </p>
 
         <p>
           <code>false</code>.
       </column>
 
+      <column name="bfd" key="check_tnl_key" type='{"type": "boolean"}'>
+          When set to true, Check Tunnel Key will make BFD only accept control
+          messages with an <code>in_key</code> of zero. Defaults to
+          <code>false</code>.
+      </column>
+
       <column name="bfd_status" key="state"
           type='{"type": "string",
           "enum": ["set", ["admin_down", "down", "init", "up"]]}'>
index 472159e..9aedaf6 100644 (file)
@@ -81,8 +81,10 @@ debugging.  The sources for the extensions are in
         Collect networking information relevant to Open vSwitch.  Runs
         the following scripts, which are described below:
 
+            * ovs-bugtool-bfd-show
             * ovs-bugtool-cfm-show
             * ovs-bugtool-lacp-show
+            * ovs-bugtool-list-dbs
             * ovs-bugtool-ovsdb-dump
             * ovs-bugtool-tc-class-show
             * ovs-bugtool-bond-show
@@ -111,6 +113,10 @@ A number of scripts are installed in /usr/share/openvswitch/scripts to
 assist Open vSwitch's xen-bugtool extensions.  The sources for the
 scripts are located in ../utilities/bugtool:
 
+    ovs-bugtool-bfd-show
+
+        Script to dump detailed BFD information for all enabled interfaces.
+
     ovs-bugtool-cfm-show
 
         Script to dump detailed CFM information for all enabled interfaces.
@@ -119,6 +125,10 @@ scripts are located in ../utilities/bugtool:
 
         Script to dump detailed LACP information for all enabled ports.
 
+    ovs-bugtool-list-dbs
+
+        Script to list the databases controlled by ovsdb-server.
+
     ovs-bugtool-ovsdb-dump
 
         Script to dump contents of Open vSwitch configuration database 
index dff18d0..4d3b8fa 100644 (file)
@@ -452,6 +452,7 @@ exit 0
 /usr/share/man/man8/ovs-vsctl.8.gz
 /usr/share/man/man8/ovs-vswitchd.8.gz
 /var/lib/openvswitch
+/var/log/openvswitch
 %exclude /usr/lib/xsconsole/plugins-base/*.py[co]
 %exclude /usr/share/openvswitch/scripts/*.py[co]
 %exclude /usr/share/openvswitch/python/*.py[co]