Merge branch 'mainstream'
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Fri, 3 Jan 2014 11:01:47 +0000 (12:01 +0100)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Fri, 3 Jan 2014 11:01:47 +0000 (12:01 +0100)
Conflicts:
Makefile.am

201 files changed:
.gitignore
AUTHORS
CodingStyle
FAQ
INSTALL
INSTALL.NetBSD [new file with mode: 0644]
Makefile.am
NEWS
NOTICE
acinclude.m4
build-aux/.gitignore
configure.ac
datapath/datapath.c
datapath/datapath.h
datapath/flow.c
datapath/flow_table.c
datapath/linux/.gitignore
datapath/linux/Modules.mk
datapath/linux/compat/flow_dissector.c
datapath/linux/compat/gre.c
datapath/linux/compat/hash-x86.c [new file with mode: 0644]
datapath/linux/compat/hash.c [new file with mode: 0644]
datapath/linux/compat/include/asm/hash.h [new file with mode: 0644]
datapath/linux/compat/include/linux/bug.h [new file with mode: 0644]
datapath/linux/compat/include/linux/hash.h [new file with mode: 0644]
datapath/linux/compat/include/linux/netdevice.h
datapath/linux/compat/include/linux/rtnetlink.h
datapath/linux/compat/include/linux/skbuff.h
datapath/linux/compat/include/net/gre.h
datapath/linux/compat/netdevice.c
datapath/linux/compat/skbuff-openvswitch.c
datapath/vport.c
datapath/vport.h
debian/changelog
debian/copyright.in
include/automake.mk
include/linux/openvswitch.h
include/windows/automake.mk [new file with mode: 0644]
include/windows/syslog.h [new file with mode: 0644]
include/windows/windefs.h [new file with mode: 0644]
lib/.gitignore
lib/async-append-null.c
lib/automake.mk
lib/bfd.c
lib/bitmap.c
lib/bitmap.h
lib/bundle.c
lib/cfm.c
lib/classifier.c
lib/classifier.h
lib/compiler.h
lib/connectivity.c [new file with mode: 0644]
lib/connectivity.h [new file with mode: 0644]
lib/coverage.c
lib/coverage.h
lib/daemon.c
lib/dpif-linux.c
lib/dpif-netdev.c
lib/dpif-provider.h
lib/dpif.c
lib/dpif.h
lib/entropy.c
lib/fatal-signal.c
lib/flow.c
lib/flow.h
lib/jhash.c
lib/json.c
lib/lacp.c
lib/mac-learning.c
lib/match.c
lib/meta-flow.c
lib/meta-flow.h
lib/multipath.c
lib/netdev-bsd.c
lib/netdev-dummy.c
lib/netdev-linux.c
lib/netdev-provider.h
lib/netdev-vport.c
lib/netdev.c
lib/netdev.h
lib/netlink.c
lib/odp-execute.c
lib/odp-execute.h
lib/odp-util.c
lib/odp-util.h
lib/ofp-actions.c
lib/ofp-actions.h
lib/ofp-msgs.c
lib/ofp-parse.c
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.h
lib/ofpbuf.c
lib/ofpbuf.h
lib/ovs-thread.c
lib/ovs-thread.h
lib/ovsdb-data.c
lib/ovsdb-idl.c
lib/ovsdb-types.c
lib/packets.h
lib/pcap-file.c
lib/pcap-file.h
lib/random.c
lib/random.h
lib/rconn.c
lib/rconn.h
lib/reconnect.c
lib/stdio.c [new file with mode: 0644]
lib/stdio.h.in [new file with mode: 0644]
lib/stp.c
lib/stream-fd.c
lib/stream-provider.h
lib/stream-ssl.c
lib/stream-tcp.c
lib/stream.c
lib/string.h.in [moved from lib/string.h with 86% similarity]
lib/timeval.c
lib/util.c
lib/util.h
lib/vconn-provider.h
lib/vconn-stream.c
lib/vconn.c
lib/vconn.h
lib/vlog.c
lib/vlog.h
m4/.gitignore [new file with mode: 0644]
m4/absolute-header.m4 [new file with mode: 0644]
m4/automake.mk [new file with mode: 0644]
m4/include_next.m4 [new file with mode: 0644]
m4/openvswitch.m4
ofproto/automake.mk
ofproto/bond.c
ofproto/connmgr.c
ofproto/netflow.c
ofproto/netflow.h
ofproto/ofproto-dpif-governor.c [deleted file]
ofproto/ofproto-dpif-governor.h [deleted file]
ofproto/ofproto-dpif-mirror.c
ofproto/ofproto-dpif-mirror.h
ofproto/ofproto-dpif-monitor.c
ofproto/ofproto-dpif-monitor.h
ofproto/ofproto-dpif-unixctl.man
ofproto/ofproto-dpif-upcall.c
ofproto/ofproto-dpif-upcall.h
ofproto/ofproto-dpif-xlate.c
ofproto/ofproto-dpif-xlate.h
ofproto/ofproto-dpif.c
ofproto/ofproto-provider.h
ofproto/ofproto.c
ofproto/ofproto.h
ofproto/tunnel.c
ovsdb/automake.mk
ovsdb/condition.c
ovsdb/log.c
ovsdb/mutation.c
ovsdb/ovsdb-client.1.in
ovsdb/ovsdb-client.c
rhel/openvswitch-fedora.spec.in
rhel/openvswitch-kmod-fedora.spec.in
rhel/openvswitch.spec.in
tests/automake.mk
tests/bfd.at
tests/cfm.at
tests/classifier.at
tests/daemon-py.at
tests/lacp.at
tests/learn.at
tests/library.at
tests/ofp-print.at
tests/ofproto-dpif.at
tests/ofproto.at
tests/ovs-ofctl.at
tests/ovsdb-server.at
tests/test-classifier.c
tests/test-flows.c
tests/test-heap.c
tests/test-multipath.c
tests/test-reconnect.c
tests/test-util.c
tests/tunnel.at
third-party/ofp-tcpdump.patch
utilities/automake.mk
utilities/ovs-appctl.c
utilities/ovs-check-dead-ifs.in
utilities/ovs-dev.py
utilities/ovs-dpctl.c
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c
utilities/ovs-vlan-bug-workaround.c
utilities/ovs-vsctl.c
vswitchd/automake.mk
vswitchd/bridge.c
vswitchd/bridge.h
vswitchd/ovs-vswitchd.c
vswitchd/vswitch.ovsschema
vswitchd/vswitch.xml
vtep/automake.mk
vtep/vtep-ctl.c
vtep/vtep.ovsschema
vtep/vtep.xml
xenserver/openvswitch-xen.spec.in

index 46b6387..f291668 100644 (file)
 /depcomp
 /distfiles
 /install-sh
+/libtool
 /manpage-check
 /missing
 /missing-distfiles
 /package.m4
 /stamp-h1
+/_build-gcc
+/_build-clang
 Module.symvers
 TAGS
 cscope.*
diff --git a/AUTHORS b/AUTHORS
index 1c2d9ea..5dccb6b 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -39,6 +39,7 @@ Edouard Bourguignon     madko@linuxed.net
 Edward Tomasz NapieraÅ‚a trasz@freebsd.org
 Ethan Jackson           ethan@nicira.com
 Flavio Leitner          fbl@redhat.com
+Francesco Fusco         ffusco@redhat.com
 FUJITA Tomonori         fujita.tomonori@lab.ntt.co.jp
 Gaetano Catalli         gaetano.catalli@gmail.com
 Geoffrey Wossum         gwossum@acm.org
@@ -48,6 +49,7 @@ Guolin Yang             gyang@nicira.com
 Gurucharan Shetty       gshetty@nicira.com
 Henry Mai               hmai@nicira.com
 Hao Zheng               hzheng@nicira.com
+Helmut Schaa            helmut.schaa@googlemail.com
 Ian Campbell            Ian.Campbell@citrix.com
 Isaku Yamahata          yamahata@valinux.co.jp
 James P.                roampune@gmail.com
@@ -110,6 +112,7 @@ Valient Gough           vgough@pobox.com
 Vivien Bernet-Rollande  vbr@soprive.net
 Wei Yongjun             yjwei@cn.fujitsu.com
 William Fulton
+YAMAMOTO Takashi        yamamoto@valinux.co.jp
 Yasuhito Takamiya       yasuhito@gmail.com
 Yu Zhiguo               yuzg@cn.fujitsu.com
 ZhengLingyun            konghuarukhr@163.com
@@ -132,6 +135,7 @@ Amey Bhide              abhide@nicira.com
 Amre Shakimov           ashakimov@vmware.com
 André Ruß               andre.russ@hybris.com
 Andreas Beckmann        debian@abeckmann.de
+Anton Matsiuk           anton.matsiuk@gmail.com
 Atzm Watanabe           atzm@stratosphere.co.jp
 Bastian Blank           waldi@debian.org
 Ben Basler              bbasler@nicira.com
@@ -167,6 +171,7 @@ Henrik Amren            henrik@nicira.com
 Hiroshi Tanaka          htanaka@nicira.com
 Hiroshi Miyata          miyahiro.dazu@gmail.com
 Igor Ganichev           iganichev@nicira.com
+Igor Sever              igor@xorops.com
 Jacob Cherkas           jcherkas@nicira.com
 Jad Naous               jnaous@gmail.com
 Jamal Hadi Salim        hadi@cyberus.ca
@@ -179,6 +184,7 @@ Jeff Merrick            jmerrick@vmware.com
 Jeongkeun Lee           jklee@hp.com
 Jian Qiu                swordqiu@gmail.com
 Joan Cirer              joan@ev0.net
+John Darrington         john@darrington.wattle.id.au
 John Galgay             john@galgay.net
 John Hurley             john.hurley@netronome.com
 Kevin Mancuso           kevin.mancuso@rackspace.com
@@ -205,6 +211,7 @@ Niklas Andersson        nandersson@nicira.com
 Padmanabhan Krishnan    kprad1@yahoo.com
 Pankaj Thakkar          thakkar@nicira.com
 Paulo Cravero           pcravero@as2594.net
+Pawan Shukla            shuklap@vmware.com
 Peter Balland           peter@nicira.com
 Peter Phaal             peter.phaal@inmon.com
 Prabina Pattnaik        Prabina.Pattnaik@nechclst.in
@@ -230,10 +237,10 @@ Teemu Koponen           koponen@nicira.com
 Timothy Chen            tchen@nicira.com
 Torbjorn Tornkvist      kruskakli@gmail.com
 Valentin Bud            valentin@hackaserver.com
+Vasiliy Tolstov         v.tolstov@selfip.ru
 Vishal Swarankar        vishal.swarnkar@gmail.com
 Vjekoslav Brajkovic     balkan@cs.washington.edu
 Voravit T.              voravit@kth.se
-YAMAMOTO Takashi        yamamoto@valinux.co.jp
 Yeming Zhao             zhaoyeming@gmail.com
 Ying Chen               yingchen@vmware.com
 Yongqiang Liu           liuyq7809@gmail.com
index c6e1a61..6ac0316 100644 (file)
@@ -224,7 +224,7 @@ statement, that is, write "return 0;" and not "return(0);"
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
   "switch" statements with very short, uniform cases may use an
diff --git a/FAQ b/FAQ
index 2912ae3..1edcd94 100644 (file)
--- a/FAQ
+++ b/FAQ
@@ -173,22 +173,17 @@ Q: Should userspace or kernel be upgraded first to minimize downtime?
 Q: What features are not available in the Open vSwitch kernel datapath
    that ships as part of the upstream Linux kernel?
 
-A: The kernel module in upstream Linux 3.3 and later does not include
-   tunnel virtual ports, that is, interfaces with type "gre",
-   "ipsec_gre", "gre64", "ipsec_gre64", "vxlan", or "lisp".  It is
-   possible to create tunnels in Linux and attach them to Open vSwitch
-   as system devices.  However, they cannot be dynamically created
-   through the OVSDB protocol or set the tunnel ids as a flow action.
-
-   Work is in progress in adding tunnel virtual ports to the upstream
-   Linux version of the Open vSwitch kernel module.  For now, if you
-   need these features, use the kernel module from the Open vSwitch
+A: The kernel module in upstream Linux does not include support for
+   LISP. Work is in progress to add support for LISP to the upstream
+   Linux version of the Open vSwitch kernel module. For now, if you
+   need this feature, use the kernel module from the Open vSwitch
    distribution instead of the upstream Linux kernel module.
 
-   The upstream kernel module does not include patch ports, but this
-   only matters for Open vSwitch 1.9 and earlier, because Open vSwitch
-   1.10 and later implement patch ports without using this kernel
-   feature.
+   Certain features require kernel support to function or to have
+   reasonable performance. If the ovs-vswitchd log file indicates that
+   a feature is not supported, consider upgrading to a newer upstream
+   Linux release or using the kernel module paired with the userspace
+   distribution.
 
 Q: What features are not available when using the userspace datapath?
 
@@ -723,6 +718,19 @@ A: Did you install OpenFlow flows that use your queues?  This is the
 
    Refer to the previous question for an example.
 
+Q: I'd like to take advantage of some QoS feature that Open vSwitch
+   doesn't yet support.  How do I do that?
+
+A: Open vSwitch does not implement QoS itself.  Instead, it can
+   configure some, but not all, of the QoS features built into the
+   Linux kernel.  If you need some QoS feature that OVS cannot
+   configure itself, then the first step is to figure out whether
+   Linux QoS supports that feature.  If it does, then you can submit a
+   patch to support Open vSwitch configuration for that feature, or
+   you can use "tc" directly to configure the feature in Linux.  (If
+   Linux QoS doesn't support the feature you want, then first you have
+   to add that support to Linux.)
+
 Q: I configured QoS, correctly, but my measurements show that it isn't
    working as well as I expect.
 
@@ -935,6 +943,50 @@ A: Yes.  Use an "internal port" configured as an access port.  For
        ovs-vsctl add-port br0 vlan9 tag=9 -- set interface vlan9 type=internal
        ifconfig vlan9 192.168.0.7
 
+   See also the following question.
+
+Q: I configured one IP address on VLAN 0 and another on VLAN 9, like
+   this:
+
+       ovs-vsctl add-br br0
+       ovs-vsctl add-port br0 eth0
+       ifconfig br0 192.168.0.5
+       ovs-vsctl add-port br0 vlan9 tag=9 -- set interface vlan9 type=internal
+       ifconfig vlan9 192.168.0.9
+
+   but other hosts that are only on VLAN 0 can reach the IP address
+   configured on VLAN 9.  What's going on?
+
+A: RFC 1122 section 3.3.4.2 "Multihoming Requirements" describes two
+   approaches to IP address handling in Internet hosts:
+
+       - In the "Strong ES Model", where an ES is a host ("End
+         System"), an IP address is primarily associated with a
+         particular interface.  The host discards packets that arrive
+         on interface A if they are destined for an IP address that is
+         configured on interface B.  The host never sends packets from
+         interface A using a source address configured on interface B.
+
+       - In the "Weak ES Model", an IP address is primarily associated
+         with a host.  The host accepts packets that arrive on any
+         interface if they are destined for any of the host's IP
+         addresses, even if the address is configured on some
+         interface other than the one on which it arrived.  The host
+         does not restrict itself to sending packets from an IP
+         address associated with the originating interface.
+
+   Linux uses the weak ES model.  That means that when packets
+   destined to the VLAN 9 IP address arrive on eth0 and are bridged to
+   br0, the kernel IP stack accepts them there for the VLAN 9 IP
+   address, even though they were not received on vlan9, the network
+   device for vlan9.
+
+   To simulate the strong ES model on Linux, one may add iptables rule
+   to filter packets based on source and destination address and
+   adjust ARP configuration with sysctls.
+
+   BSD uses the strong ES model.
+
 Q: My OpenFlow controller doesn't see the VLANs that I expect.
 
 A: The configuration for VLANs in the Open vSwitch database (e.g. via
@@ -1030,7 +1082,7 @@ A: Open vSwitch 1.9 and earlier support only OpenFlow 1.0 (plus
 
        ovs-vsctl set bridge br0 protocols=OpenFlow10,OpenFlow12,OpenFlow13
 
-   Open vSwitch version 1.12 and later will have experimental support
+   Open vSwitch version 2.0 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:
diff --git a/INSTALL b/INSTALL
index c8b1aa7..95ee898 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -9,6 +9,7 @@ on a specific platform, please see one of these files:
     - INSTALL.Fedora
     - INSTALL.RHEL
     - INSTALL.XenServer
+    - INSTALL.NetBSD
 
 Build Requirements
 ------------------
@@ -198,6 +199,11 @@ Prerequisites section, follow the procedure below to build.
 
       % ./configure --with-linux=/path/to/linux KARCH=mips
 
+   If you plan to do much Open vSwitch development, you might want to
+   add --enable-Werror, which adds the -Werror option to the compiler
+   command line, turning warnings into errors.  That makes it
+   impossible to miss warnings generated by the build.
+
    The configure script accepts a number of other options and honors
    additional environment variables.  For a full list, invoke
    configure with the --help option.
diff --git a/INSTALL.NetBSD b/INSTALL.NetBSD
new file mode 100644 (file)
index 0000000..49782a8
--- /dev/null
@@ -0,0 +1,32 @@
+           How to Install Open vSwitch on NetBSD
+           =====================================
+
+On NetBSD, you might want to install requirements from pkgsrc.
+In that case, you need at least the following packages.
+
+       automake
+       libtool-base
+       gmake
+       python27
+       py27-xml
+       pkg_alternatives
+
+Some components (eg. ovsdbmonitor) have additional requirements.
+(See INSTALL)
+
+Assuming you are running NetBSD/amd64 6.1.2, you can download and
+install pre-built binary packages as the following.
+(You might get some warnings about minor version mismatch.  Don't care.)
+
+    # PKG_PATH=http://ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/amd64/6.1.2/All/
+    # export PKG_PATH
+    # pkg_add automake libtool-base gmake python27 py27-xml pkg_alternatives
+
+NetBSD's /usr/bin/make is not GNU make.  GNU make is installed as
+/usr/pkg/bin/gmake by the above mentioned 'gmake' package.
+
+As all executables installed with pkgsrc are placed in /usr/pkg/bin/
+directory, it might be a good idea to add it to your PATH.
+
+Open vSwitch on NetBSD is currently "userspace switch" implementation
+in the sense described in INSTALL.userspace and PORTING.
index adb901b..670f5f0 100644 (file)
@@ -10,6 +10,11 @@ ACLOCAL_AMFLAGS = -I m4
 SUBDIRS = datapath
 
 AM_CPPFLAGS = $(SSL_CFLAGS)
+
+if WIN32
+AM_CPPFLAGS += -I $(top_srcdir)/include/windows
+endif
+
 AM_CPPFLAGS += -I $(top_srcdir)/include
 AM_CPPFLAGS += -I $(top_srcdir)/lib
 AM_CPPFLAGS += -I $(top_builddir)/lib
@@ -56,6 +61,7 @@ EXTRA_DIST = \
        INSTALL.Fedora \
        INSTALL.KVM \
        INSTALL.Libvirt \
+       INSTALL.NetBSD \
        INSTALL.RHEL \
        INSTALL.SSL \
        INSTALL.XenServer \
@@ -93,7 +99,7 @@ MAN_FRAGMENTS =
 MAN_ROOTS =
 noinst_DATA =
 noinst_HEADERS =
-noinst_LIBRARIES =
+lib_LTLIBRARIES =
 noinst_man_MANS =
 noinst_PROGRAMS =
 noinst_SCRIPTS =
@@ -159,10 +165,10 @@ dist-hook-git: distfiles
        @if test -e $(srcdir)/.git && (git --version) >/dev/null 2>&1; then \
          (cd datapath && $(MAKE) distfiles);                               \
          (cat distfiles; sed 's|^|datapath/|' datapath/distfiles) |        \
-           sort -u > all-distfiles;                                        \
+           LC_ALL=C sort -u > all-distfiles;                                       \
          (cd $(srcdir) && git ls-files) | grep -vFf $(srcdir)/.non-distfiles |     \
-           sort -u > all-gitfiles;                                         \
-         comm -1 -3 all-distfiles all-gitfiles > missing-distfiles;        \
+           LC_ALL=C sort -u > all-gitfiles;                                        \
+         LC_ALL=C comm -1 -3 all-distfiles all-gitfiles > missing-distfiles;       \
          if test -s missing-distfiles; then                                \
            echo "The distribution is missing the following files:";        \
            cat missing-distfiles;                                          \
@@ -177,7 +183,8 @@ distfiles: Makefile
        list='$(DISTFILES)'; \
        for file in $$list; do echo $$file; done | \
          sed -e "s|^$$srcdirstrip/||;t" \
-             -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t" | sort -u > $@
+             -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t" | \
+          LC_ALL=C sort -u > $@
 CLEANFILES += distfiles
 endif
 .PHONY: dist-hook-git
@@ -284,6 +291,7 @@ if LINUX_ENABLED
        cd datapath/linux && $(MAKE) modules_install
 endif
 
+include m4/automake.mk
 include lib/automake.mk
 include ofproto/automake.mk
 include utilities/automake.mk
diff --git a/NEWS b/NEWS
index 8556083..515a236 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,32 @@
-Post-v2.0.0
+Post-v2.1.0
 ---------------------
+
+
+v2.1.0 - xx xxx xxxx
+---------------------
+   - Address prefix tracking support for flow tables.  New columns
+     "prefixes" in OVS-DB table "Flow_Table" controls which packet
+     header fields are used for address prefix tracking.  Prefix
+     tracking allows the classifier to skip rules with longer than
+     necessary prefixes, resulting in better wildcarding for datapath
+     flows.  Default configuration is to not use any fields for prefix
+     tracking.  However, if any flow tables contain both exact matches
+     and masked matches for IP address fields, OVS performance may be
+     increased by using this feature.
+     * As of now, the fields for which prefix lookup can be enabled
+       are: 'tun_id', 'tun_src', 'tun_dst', 'nw_src', 'nw_dst' (or
+       aliases 'ip_src' and 'ip_dst'), 'ipv6_src', and 'ipv6_dst'.
+       (Using this feature for 'tun_id' would only make sense if the
+       tunnel IDs have prefix structure similar to IP addresses.)
+     * There is a maximum number of fields that can be enabled for any
+       one flow table.  Currently this limit is 3.
+     * Examples:
+       $ ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- \
+         --id=@N1 create Flow_Table name=table0
+       $ ovs-vsctl set Bridge br0 flow_tables:1=@N1 -- \
+         --id=@N1 create Flow_Table name=table1
+       $ ovs-vsctl set Flow_Table table0 prefixes=ip_dst,ip_src
+       $ ovs-vsctl set Flow_Table table1 prefixes=[]
    - TCP flags matching: OVS now supports matching of TCP flags.  This
      has an adverse performance impact when using OVS userspace 1.10
      or older (no megaflows support) together with the new OVS kernel
@@ -14,16 +41,28 @@ Post-v2.0.0
      * OVS limits the OpenFlow port numbers it assigns to port 32767 and
        below, leaving port numbers above that range free for assignment
        by the controller.
+     * ovs-vswitchd now honors changes to the "ofport_request" column
+       in the Interface table by changing the port's OpenFlow port
+       number.
    - ovs-vswitchd.conf.db.5 man page will contain graphviz/dot
      diagram only if graphviz package was installed at the build time.
    - Support for Linux kernels up to 3.11
    - ovs-dpctl:
      The "show" command also displays mega flow mask stats.
+   - ovs-ofctl:
+     * New command "ofp-parse-pcap" to dump OpenFlow from PCAP files.
    - ovs-controller has been renamed test-controller.  It is no longer
      packaged or installed by default, because too many users assumed
      incorrectly that ovs-controller was a necessary or desirable part
      of an Open vSwitch deployment.
    - Added vlog option to export to a UDP syslog sink.
+   - ovsdb-client:
+     * The "monitor" command can now monitor all tables in a database,
+       instead of being limited to a single table.
+   - The flow-eviction-threshold has been replaced by the flow-limit which is a
+     hard limit on the number of flows in the datapath.  It defaults to 200,000
+     flows.  OVS automatically adjusts this number depending on network
+     conditions.
 
 
 v2.0.0 - 15 Oct 2013
diff --git a/NOTICE b/NOTICE
index dafd25f..7a3d660 100644 (file)
--- a/NOTICE
+++ b/NOTICE
@@ -2,7 +2,7 @@ This file is included in compliance with the Apache 2.0 license,
 available at http://www.apache.org/licenses/LICENSE-2.0.html
 
 Open vSwitch
-Copyright (c) 2007, 2008, 2009, 2010, 2011 Nicira, Inc.
+Copyright (c) 2007, 2008, 2009, 2010, 2011, 2013 Nicira, Inc.
 
 Open vSwitch BSD port
 Copyright (c) 2011 Gaetano Catalli
@@ -19,3 +19,6 @@ Illinois at Urbana-Champaign.
 
 lib/ovs.tmac includes troff macros written by Eric S. Raymond
 and Werner Lemberg.
+
+m4/include_next.m4 and m4/absolute-header.m4
+Copyright (C) 2006-2013 Free Software Foundation, Inc.
index 94f9b61..7078654 100644 (file)
@@ -228,10 +228,12 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
   OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [dev_get_by_index_rcu])
   OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [__skb_gso_segment])
   OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [can_checksum_protocol])
+  OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [netdev_features_t])
 
   OVS_GREP_IFELSE([$KSRC/include/linux/rcupdate.h], [rcu_read_lock_held], [],
                   [OVS_GREP_IFELSE([$KSRC/include/linux/rtnetlink.h],
                                    [rcu_read_lock_held])])
+  OVS_GREP_IFELSE([$KSRC/include/linux/rtnetlink.h], [lockdep_rtnl_is_held])
   
   # Check for the proto_data_valid member in struct sk_buff.  The [^@]
   # is necessary because some versions of this header remove the
@@ -256,6 +258,8 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
                   [OVS_DEFINE([HAVE_SKB_WARN_LRO])])
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [consume_skb])
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_frag_page])
+  OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_has_frag_list])
+  OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [__skb_fill_page_desc])
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_reset_mac_len])
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_unclone])
 
@@ -270,6 +274,7 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
   OVS_GREP_IFELSE([$KSRC/include/net/checksum.h], [csum_unfold])
 
   OVS_GREP_IFELSE([$KSRC/include/net/genetlink.h], [parallel_ops])
+  OVS_GREP_IFELSE([$KSRC/include/net/gre.h], [gre_cisco_register])
   OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_get_be16])
   OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_put_be16])
   OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_put_be32])
@@ -523,7 +528,7 @@ AC_DEFUN([OVS_ENABLE_SPARSE],
    AC_SUBST([SPARSE])
    AC_CONFIG_COMMANDS_PRE(
      [if test $ovs_cv_gnu_make_if = yes; then
-        CC='$(if $(C),REAL_CC="'"$CC"'" CHECK="$(SPARSE) -I $(top_srcdir)/include/sparse $(SPARSEFLAGS) $(SPARSE_EXTRA_INCLUDES) " cgcc $(CGCCFLAGS),'"$CC"')'
+        CC='$(if $(C),env REAL_CC="'"$CC"'" CHECK="$(SPARSE) -I $(top_srcdir)/include/sparse $(SPARSEFLAGS) $(SPARSE_EXTRA_INCLUDES) " cgcc $(CGCCFLAGS),'"$CC"')'
       fi])])
 
 dnl OVS_PTHREAD_SET_NAME
index 999eae2..4c13ba3 100644 (file)
@@ -1,4 +1,7 @@
 /compile
+/config.guess
+/config.sub
 /depcomp
 /install-sh
+/ltmain.sh
 /missing
index 167cc71..f6a11f0 100644 (file)
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 AC_PREREQ(2.64)
-AC_INIT(openvswitch, 2.0.90, ovs-bugs@openvswitch.org)
+AC_INIT(openvswitch, 2.1.90, ovs-bugs@openvswitch.org)
 AC_CONFIG_SRCDIR([datapath/datapath.c])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_AUX_DIR([build-aux])
@@ -24,7 +24,6 @@ AM_INIT_AUTOMAKE
 AC_PROG_CC
 AM_PROG_CC_C_O
 AC_PROG_CPP
-AC_PROG_RANLIB
 AC_PROG_MKDIR_P
 AC_PROG_FGREP
 AC_PROG_EGREP
@@ -41,6 +40,7 @@ AC_USE_SYSTEM_EXTENSIONS
 AC_C_BIGENDIAN
 AC_SYS_LARGEFILE
 
+LT_INIT([disable-shared])
 AC_SEARCH_LIBS([pow], [m])
 AC_SEARCH_LIBS([clock_gettime], [rt])
 AC_SEARCH_LIBS([timer_create], [rt])
@@ -78,7 +78,6 @@ OVS_CHECK_BACKTRACE
 OVS_CHECK_MALLOC_HOOKS
 OVS_CHECK_VALGRIND
 OVS_CHECK_SOCKET_LIBS
-OVS_CHECK_LINKER_SECTIONS
 OVS_CHECK_XENSERVER_VERSION
 OVS_CHECK_GROFF
 OVS_CHECK_GNU_MAKE
@@ -92,6 +91,9 @@ OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(8)
 OVS_CHECK_POSIX_AIO
 OVS_CHECK_PTHREAD_SET_NAME
 
+OVS_CHECK_INCLUDE_NEXT([stdio.h string.h])
+AC_CONFIG_FILES([lib/stdio.h lib/string.h])
+
 OVS_ENABLE_OPTION([-Wall])
 OVS_ENABLE_OPTION([-Wextra])
 OVS_ENABLE_OPTION([-Wno-sign-compare])
index 1808c36..b42fd8b 100644 (file)
@@ -110,10 +110,9 @@ int lockdep_ovsl_is_held(void)
 #endif
 
 static struct vport *new_vport(const struct vport_parms *);
-static int queue_gso_packets(struct net *, int dp_ifindex, struct sk_buff *,
+static int queue_gso_packets(struct datapath *dp, struct sk_buff *,
                             const struct dp_upcall_info *);
-static int queue_userspace_packet(struct net *, int dp_ifindex,
-                                 struct sk_buff *,
+static int queue_userspace_packet(struct datapath *dp, struct sk_buff *,
                                  const struct dp_upcall_info *);
 
 /* Must be called with rcu_read_lock or ovs_mutex. */
@@ -279,7 +278,6 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
                  const struct dp_upcall_info *upcall_info)
 {
        struct dp_stats_percpu *stats;
-       int dp_ifindex;
        int err;
 
        if (upcall_info->portid == 0) {
@@ -287,16 +285,10 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
                goto err;
        }
 
-       dp_ifindex = get_dpifindex(dp);
-       if (!dp_ifindex) {
-               err = -ENODEV;
-               goto err;
-       }
-
        if (!skb_is_gso(skb))
-               err = queue_userspace_packet(ovs_dp_get_net(dp), dp_ifindex, skb, upcall_info);
+               err = queue_userspace_packet(dp, skb, upcall_info);
        else
-               err = queue_gso_packets(ovs_dp_get_net(dp), dp_ifindex, skb, upcall_info);
+               err = queue_gso_packets(dp, skb, upcall_info);
        if (err)
                goto err;
 
@@ -312,8 +304,7 @@ err:
        return err;
 }
 
-static int queue_gso_packets(struct net *net, int dp_ifindex,
-                            struct sk_buff *skb,
+static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
                             const struct dp_upcall_info *upcall_info)
 {
        unsigned short gso_type = skb_shinfo(skb)->gso_type;
@@ -322,14 +313,14 @@ static int queue_gso_packets(struct net *net, int dp_ifindex,
        struct sk_buff *segs, *nskb;
        int err;
 
-       segs = __skb_gso_segment(skb, NETIF_F_SG | NETIF_F_HW_CSUM, false);
+       segs = __skb_gso_segment(skb, NETIF_F_SG, false);
        if (IS_ERR(segs))
                return PTR_ERR(segs);
 
        /* Queue all of the segments. */
        skb = segs;
        do {
-               err = queue_userspace_packet(net, dp_ifindex, skb, upcall_info);
+               err = queue_userspace_packet(dp, skb, upcall_info);
                if (err)
                        break;
 
@@ -382,11 +373,11 @@ static size_t key_attr_size(void)
                + nla_total_size(28); /* OVS_KEY_ATTR_ND */
 }
 
-static size_t upcall_msg_size(const struct sk_buff *skb,
-                             const struct nlattr *userdata)
+static size_t upcall_msg_size(const struct nlattr *userdata,
+                             unsigned int hdrlen)
 {
        size_t size = NLMSG_ALIGN(sizeof(struct ovs_header))
-               + nla_total_size(skb->len) /* OVS_PACKET_ATTR_PACKET */
+               + nla_total_size(hdrlen) /* OVS_PACKET_ATTR_PACKET */
                + nla_total_size(key_attr_size()); /* OVS_PACKET_ATTR_KEY */
 
        /* OVS_PACKET_ATTR_USERDATA */
@@ -396,8 +387,7 @@ static size_t upcall_msg_size(const struct sk_buff *skb,
        return size;
 }
 
-static int queue_userspace_packet(struct net *net, int dp_ifindex,
-                                 struct sk_buff *skb,
+static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
                                  const struct dp_upcall_info *upcall_info)
 {
        struct ovs_header *upcall;
@@ -406,12 +396,17 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex,
        struct nlattr *nla;
        struct genl_info info = {
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)
-               .dst_sk = net->genl_sock,
+               .dst_sk = ovs_dp_get_net(dp)->genl_sock,
 #endif
                .snd_portid = upcall_info->portid,
        };
        size_t len;
-       int err;
+       unsigned int hlen;
+       int err, dp_ifindex;
+
+       dp_ifindex = get_dpifindex(dp);
+       if (!dp_ifindex)
+               return -ENODEV;
 
        if (vlan_tx_tag_present(skb)) {
                nskb = skb_clone(skb, GFP_ATOMIC);
@@ -432,7 +427,21 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex,
                goto out;
        }
 
-       len = upcall_msg_size(skb, upcall_info->userdata);
+       /* Complete checksum if needed */
+       if (skb->ip_summed == CHECKSUM_PARTIAL &&
+           (err = skb_checksum_help(skb)))
+               goto out;
+
+       /* Older versions of OVS user space enforce alignment of the last
+        * Netlink attribute to NLA_ALIGNTO which would require extensive
+        * padding logic. Only perform zerocopy if padding is not required.
+        */
+       if (dp->user_features & OVS_DP_F_UNALIGNED)
+               hlen = skb_zerocopy_headlen(skb);
+       else
+               hlen = skb->len;
+
+       len = upcall_msg_size(upcall_info->userdata, hlen);
        user_skb = genlmsg_new_unicast(len, &info, GFP_ATOMIC);
        if (!user_skb) {
                err = -ENOMEM;
@@ -452,13 +461,19 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex,
                          nla_len(upcall_info->userdata),
                          nla_data(upcall_info->userdata));
 
-       nla = __nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, skb->len);
+       /* Only reserve room for attribute header, packet data is added
+        * in skb_zerocopy() */
+       if (!(nla = nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, 0))) {
+               err = -ENOBUFS;
+               goto out;
+       }
+       nla->nla_len = nla_attr_size(skb->len);
 
-       skb_copy_and_csum_dev(skb, nla_data(nla));
+       skb_zerocopy(user_skb, skb, skb->len, hlen);
 
-       genlmsg_end(user_skb, upcall);
-       err = genlmsg_unicast(net, user_skb, upcall_info->portid);
+       ((struct nlmsghdr *) user_skb->data)->nlmsg_len = user_skb->len;
 
+       err = genlmsg_unicast(ovs_dp_get_net(dp), user_skb, upcall_info->portid);
 out:
        kfree_skb(nskb);
        return err;
@@ -1045,6 +1060,7 @@ static struct genl_ops dp_flow_genl_ops[] = {
 static const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = {
        [OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
        [OVS_DP_ATTR_UPCALL_PID] = { .type = NLA_U32 },
+       [OVS_DP_ATTR_USER_FEATURES] = { .type = NLA_U32 },
 };
 
 static struct genl_family dp_datapath_genl_family = {
@@ -1103,6 +1119,9 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
                        &dp_megaflow_stats))
                goto nla_put_failure;
 
+       if (nla_put_u32(skb, OVS_DP_ATTR_USER_FEATURES, dp->user_features))
+               goto nla_put_failure;
+
        return genlmsg_end(skb, ovs_header);
 
 nla_put_failure:
@@ -1149,6 +1168,24 @@ static struct datapath *lookup_datapath(struct net *net,
        return dp ? dp : ERR_PTR(-ENODEV);
 }
 
+static void ovs_dp_reset_user_features(struct sk_buff *skb, struct genl_info *info)
+{
+       struct datapath *dp;
+
+       dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
+       if (!dp)
+               return;
+
+       WARN(dp->user_features, "Dropping previously announced user features\n");
+       dp->user_features = 0;
+}
+
+static void ovs_dp_change(struct datapath *dp, struct nlattr **a)
+{
+       if (a[OVS_DP_ATTR_USER_FEATURES])
+               dp->user_features = nla_get_u32(a[OVS_DP_ATTR_USER_FEATURES]);
+}
+
 static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr **a = info->attrs;
@@ -1201,12 +1238,23 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
        parms.port_no = OVSP_LOCAL;
        parms.upcall_portid = nla_get_u32(a[OVS_DP_ATTR_UPCALL_PID]);
 
+       ovs_dp_change(dp, a);
+
        vport = new_vport(&parms);
        if (IS_ERR(vport)) {
                err = PTR_ERR(vport);
                if (err == -EBUSY)
                        err = -EEXIST;
 
+               if (err == -EEXIST) {
+                       /* An outdated user space instance that does not understand
+                        * the concept of user_features has attempted to create a new
+                        * datapath and is likely to reuse it. Drop all user features.
+                        */
+                       if (info->genlhdr->version < OVS_DP_VER_FEATURES)
+                               ovs_dp_reset_user_features(skb, info);
+               }
+
                goto err_destroy_ports_array;
        }
 
@@ -1304,6 +1352,8 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
        if (IS_ERR(dp))
                goto unlock;
 
+       ovs_dp_change(dp, info->attrs);
+
        reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_NEW);
        if (IS_ERR(reply)) {
                err = PTR_ERR(reply);
index 89ba80f..b3ae7cd 100644 (file)
@@ -90,6 +90,8 @@ struct datapath {
        /* Network namespace ref. */
        struct net *net;
 #endif
+
+       u32 user_features;
 };
 
 /**
index 9b3d3a7..8be3801 100644 (file)
@@ -87,17 +87,25 @@ void ovs_flow_stats_update(struct sw_flow *flow, struct sk_buff *skb)
        spin_unlock(&stats->lock);
 }
 
-static void stats_read(struct flow_stats *stats,
+static void stats_read(struct flow_stats *stats, bool lock_bh,
                       struct ovs_flow_stats *ovs_stats,
                       unsigned long *used, __be16 *tcp_flags)
 {
-       spin_lock(&stats->lock);
+       if (lock_bh)
+               spin_lock_bh(&stats->lock);
+       else
+               spin_lock(&stats->lock);
+
        if (time_after(stats->used, *used))
                *used = stats->used;
        *tcp_flags |= stats->tcp_flags;
        ovs_stats->n_packets += stats->packet_count;
        ovs_stats->n_bytes += stats->byte_count;
-       spin_unlock(&stats->lock);
+
+       if (lock_bh)
+               spin_unlock_bh(&stats->lock);
+       else
+               spin_unlock(&stats->lock);
 }
 
 void ovs_flow_stats_get(struct sw_flow *flow, struct ovs_flow_stats *ovs_stats,
@@ -110,33 +118,38 @@ void ovs_flow_stats_get(struct sw_flow *flow, struct ovs_flow_stats *ovs_stats,
        memset(ovs_stats, 0, sizeof(*ovs_stats));
 
        if (!flow->stats.is_percpu) {
-               stats_read(flow->stats.stat, ovs_stats, used, tcp_flags);
+               stats_read(flow->stats.stat, true, ovs_stats, used, tcp_flags);
        } else {
                cur_cpu = get_cpu();
+
                for_each_possible_cpu(cpu) {
                        struct flow_stats *stats;
-
-                       if (cpu == cur_cpu)
-                               local_bh_disable();
+                       bool lock_bh;
 
                        stats = per_cpu_ptr(flow->stats.cpu_stats, cpu);
-                       stats_read(stats, ovs_stats, used, tcp_flags);
-
-                       if (cpu == cur_cpu)
-                               local_bh_enable();
+                       lock_bh = (cpu == cur_cpu);
+                       stats_read(stats, lock_bh, ovs_stats, used, tcp_flags);
                }
                put_cpu();
        }
 }
 
-static void stats_reset(struct flow_stats *stats)
+static void stats_reset(struct flow_stats *stats, bool lock_bh)
 {
-       spin_lock(&stats->lock);
+       if (lock_bh)
+               spin_lock_bh(&stats->lock);
+       else
+               spin_lock(&stats->lock);
+
        stats->used = 0;
        stats->packet_count = 0;
        stats->byte_count = 0;
        stats->tcp_flags = 0;
-       spin_unlock(&stats->lock);
+
+       if (lock_bh)
+               spin_unlock_bh(&stats->lock);
+       else
+               spin_unlock(&stats->lock);
 }
 
 void ovs_flow_stats_clear(struct sw_flow *flow)
@@ -144,19 +157,15 @@ void ovs_flow_stats_clear(struct sw_flow *flow)
        int cpu, cur_cpu;
 
        if (!flow->stats.is_percpu) {
-               stats_reset(flow->stats.stat);
+               stats_reset(flow->stats.stat, true);
        } else {
                cur_cpu = get_cpu();
 
                for_each_possible_cpu(cpu) {
+                       bool lock_bh;
 
-                       if (cpu == cur_cpu)
-                               local_bh_disable();
-
-                       stats_reset(per_cpu_ptr(flow->stats.cpu_stats, cpu));
-
-                       if (cpu == cur_cpu)
-                               local_bh_enable();
+                       lock_bh = (cpu == cur_cpu);
+                       stats_reset(per_cpu_ptr(flow->stats.cpu_stats, cpu), lock_bh);
                }
                put_cpu();
        }
index 6bb68d8..cc0a8e4 100644 (file)
@@ -25,7 +25,7 @@
 #include <linux/if_vlan.h>
 #include <net/llc_pdu.h>
 #include <linux/kernel.h>
-#include <linux/jhash.h>
+#include <linux/hash.h>
 #include <linux/jiffies.h>
 #include <linux/llc.h>
 #include <linux/module.h>
@@ -44,7 +44,6 @@
 #include <net/ipv6.h>
 #include <net/ndisc.h>
 
-#include "datapath.h"
 #include "vlan.h"
 
 #define TBL_MIN_BUCKETS                1024
@@ -390,7 +389,7 @@ static u32 flow_hash(const struct sw_flow_key *key, int key_start,
        /* Make sure number of hash bytes are multiple of u32. */
        BUILD_BUG_ON(sizeof(long) % sizeof(u32));
 
-       return jhash2(hash_key, hash_u32s, 0);
+       return arch_fast_hash2(hash_key, hash_u32s, 0);
 }
 
 static int flow_key_start(const struct sw_flow_key *key)
index 32b1770..d10d028 100644 (file)
@@ -21,6 +21,8 @@
 /genl_exec.c
 /gre.c
 /gso.c
+/hash.c
+/hash-x86.c
 /ip_output-openvswitch.c
 /ip_tunnels_core.c
 /kcompat.h
index fee132e..cedb8c9 100644 (file)
@@ -6,6 +6,8 @@ openvswitch_sources += \
        linux/compat/gre.c \
        linux/compat/gso.c \
        linux/compat/genetlink-openvswitch.c \
+       linux/compat/hash.c \
+       linux/compat/hash-x86.c \
        linux/compat/ip_tunnels_core.c \
        linux/compat/netdevice.c \
        linux/compat/net_namespace.c \
@@ -15,13 +17,16 @@ openvswitch_sources += \
        linux/compat/utils.c
 openvswitch_headers += \
        linux/compat/gso.h \
+       linux/compat/include/asm/hash.h \
        linux/compat/include/asm/percpu.h \
+       linux/compat/include/linux/bug.h \
        linux/compat/include/linux/compiler.h \
        linux/compat/include/linux/compiler-gcc.h \
        linux/compat/include/linux/cpumask.h \
        linux/compat/include/linux/err.h \
        linux/compat/include/linux/etherdevice.h \
        linux/compat/include/linux/flex_array.h \
+       linux/compat/include/linux/hash.h \
        linux/compat/include/linux/icmp.h \
        linux/compat/include/linux/icmpv6.h \
        linux/compat/include/linux/if.h \
index 7a0d09b..a8a2e52 100644 (file)
@@ -46,7 +46,7 @@ static void iph_to_flow_copy_addrs(struct flow_keys *flow, const struct iphdr *i
        memcpy(&flow->src, &iph->saddr, sizeof(flow->src) + sizeof(flow->dst));
 }
 
-__be32 skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto)
+static __be32 skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto)
 {
        int poff = proto_ports_offset(ip_proto);
 
index f35f11f..58b1e73 100644 (file)
 
 #include "gso.h"
 
-static void gre_csum_fix(struct sk_buff *skb)
-{
-       struct gre_base_hdr *greh;
-       __be32 *options;
-       int gre_offset = skb_transport_offset(skb);
+#ifndef HAVE_GRE_CISCO_REGISTER
 
-       greh = (struct gre_base_hdr *)skb_transport_header(skb);
-       options = ((__be32 *)greh + 1);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
 
-       *options = 0;
-       *(__sum16 *)options = csum_fold(skb_checksum(skb, gre_offset,
-                                                    skb->len - gre_offset, 0));
-}
+#define GREPROTO_CISCO         0
+#define GREPROTO_MAX           1
 
-struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum)
+struct gre_protocol {
+       int  (*handler)(struct sk_buff *skb);
+};
+static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
+
+static int gre_rcv(struct sk_buff *skb)
 {
-       int err;
+       const struct gre_protocol *proto;
+       u8 ver;
+       int ret;
 
-       skb_reset_inner_headers(skb);
+       if (!pskb_may_pull(skb, 12))
+               goto drop;
 
-       if (skb_is_gso(skb)) {
-               if (gre_csum)
-                       OVS_GSO_CB(skb)->fix_segment = gre_csum_fix;
-       } else {
-               if (skb->ip_summed == CHECKSUM_PARTIAL && gre_csum) {
-                       err = skb_checksum_help(skb);
-                       if (err)
-                               goto error;
+       ver = skb->data[1] & 0x7f;
+       if (ver >= GREPROTO_MAX)
+               goto drop;
 
-               } else if (skb->ip_summed != CHECKSUM_PARTIAL)
-                       skb->ip_summed = CHECKSUM_NONE;
-       }
-       return skb;
-error:
+       rcu_read_lock();
+       proto = rcu_dereference(gre_proto[ver]);
+       if (!proto || !proto->handler)
+               goto drop_unlock;
+       ret = proto->handler(skb);
+       rcu_read_unlock();
+       return ret;
+
+drop_unlock:
+       rcu_read_unlock();
+drop:
        kfree_skb(skb);
-       return ERR_PTR(err);
+       return NET_RX_DROP;
 }
 
-static bool is_gre_gso(struct sk_buff *skb)
+static const struct net_protocol net_gre_protocol = {
+       .handler     = gre_rcv,
+       .netns_ok    = 1,
+};
+
+static int gre_add_protocol(const struct gre_protocol *proto, u8 version)
 {
-       return skb_is_gso(skb);
+       if (version >= GREPROTO_MAX)
+               return -EINVAL;
+
+       if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
+               pr_err("%s: cannot register gre protocol handler\n", __func__);
+               return -EAGAIN;
+       }
+
+       return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ?
+               0 : -EBUSY;
 }
 
-void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
-                     int hdr_len)
+static int gre_del_protocol(const struct gre_protocol *proto, u8 version)
 {
-       struct gre_base_hdr *greh;
+       int ret;
 
-       __skb_push(skb, hdr_len);
+       if (version >= GREPROTO_MAX)
+               return -EINVAL;
 
-       greh = (struct gre_base_hdr *)skb->data;
-       greh->flags = tnl_flags_to_gre_flags(tpi->flags);
-       greh->protocol = tpi->proto;
+       ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ?
+               0 : -EBUSY;
 
-       if (tpi->flags & (TUNNEL_KEY | TUNNEL_CSUM | TUNNEL_SEQ)) {
-               __be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
+       if (ret)
+               return ret;
 
-               if (tpi->flags & TUNNEL_SEQ) {
-                       *ptr = tpi->seq;
-                       ptr--;
-               }
-               if (tpi->flags & TUNNEL_KEY) {
-                       *ptr = tpi->key;
-                       ptr--;
-               }
-               if (tpi->flags & TUNNEL_CSUM && !is_gre_gso(skb)) {
-                       *ptr = 0;
-                       *(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0,
-                                               skb->len, 0));
-               }
-       }
+       synchronize_net();
+
+       ret = inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
+       if (ret)
+               return ret;
+
+       return 0;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
+#endif
 
 static __sum16 check_checksum(struct sk_buff *skb)
 {
@@ -198,7 +206,6 @@ static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
 }
 
 static struct gre_cisco_protocol __rcu *gre_cisco_proto;
-
 static int gre_cisco_rcv(struct sk_buff *skb)
 {
        struct tnl_ptk_info tpi;
@@ -226,136 +233,110 @@ static const struct gre_protocol ipgre_protocol = {
        .handler        =       gre_cisco_rcv,
 };
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
-static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
-
-int gre_add_protocol(const struct gre_protocol *proto, u8 version)
+int gre_cisco_register(struct gre_cisco_protocol *newp)
 {
-       if (version >= GREPROTO_MAX)
-               return -EINVAL;
+       int err;
 
-       return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ?
+       err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO);
+       if (err) {
+               pr_warn("%s: cannot register gre_cisco protocol handler\n", __func__);
+               return err;
+       }
+
+
+       return (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, NULL, newp) == NULL) ?
                0 : -EBUSY;
 }
 
-int gre_del_protocol(const struct gre_protocol *proto, u8 version)
+int gre_cisco_unregister(struct gre_cisco_protocol *proto)
 {
        int ret;
 
-       if (version >= GREPROTO_MAX)
-               return -EINVAL;
-
-       ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ?
-               0 : -EBUSY;
+       ret = (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, proto, NULL) == proto) ?
+               0 : -EINVAL;
 
        if (ret)
                return ret;
 
        synchronize_net();
-       return 0;
+       ret = gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
+       return ret;
 }
 
-static int gre_rcv(struct sk_buff *skb)
-{
-       const struct gre_protocol *proto;
-       u8 ver;
-       int ret;
-
-       if (!pskb_may_pull(skb, 12))
-               goto drop;
+#endif /* !HAVE_GRE_CISCO_REGISTER */
 
-       ver = skb->data[1] & 0x7f;
-       if (ver >= GREPROTO_MAX)
-               goto drop;
+/* GRE TX side. */
+static void gre_csum_fix(struct sk_buff *skb)
+{
+       struct gre_base_hdr *greh;
+       __be32 *options;
+       int gre_offset = skb_transport_offset(skb);
 
-       rcu_read_lock();
-       proto = rcu_dereference(gre_proto[ver]);
-       if (!proto || !proto->handler)
-               goto drop_unlock;
-       ret = proto->handler(skb);
-       rcu_read_unlock();
-       return ret;
+       greh = (struct gre_base_hdr *)skb_transport_header(skb);
+       options = ((__be32 *)greh + 1);
 
-drop_unlock:
-       rcu_read_unlock();
-drop:
-       kfree_skb(skb);
-       return NET_RX_DROP;
+       *options = 0;
+       *(__sum16 *)options = csum_fold(skb_checksum(skb, gre_offset,
+                                                    skb->len - gre_offset, 0));
 }
 
-static const struct net_protocol net_gre_protocol = {
-       .handler     = gre_rcv,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
-       .netns_ok    = 1,
-#endif
-};
-#endif
-
-static int gre_compat_init(void)
+struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum)
 {
        int err;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
-       if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
-               pr_err("%s: cannot register gre protocol handler\n", __func__);
-               return -EAGAIN;
-       }
-#endif
-       err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO);
-       if (err) {
-               pr_warn("%s: cannot register gre_cisco protocol handler\n", __func__);
+       skb_reset_inner_headers(skb);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
-               inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
-#endif
-       }
+       if (skb_is_gso(skb)) {
+               if (gre_csum)
+                       OVS_GSO_CB(skb)->fix_segment = gre_csum_fix;
+       } else {
+               if (skb->ip_summed == CHECKSUM_PARTIAL && gre_csum) {
+                       err = skb_checksum_help(skb);
+                       if (err)
+                               goto error;
 
-       return err;
+               } else if (skb->ip_summed != CHECKSUM_PARTIAL)
+                       skb->ip_summed = CHECKSUM_NONE;
+       }
+       return skb;
+error:
+       kfree_skb(skb);
+       return ERR_PTR(err);
 }
 
-static int gre_compat_exit(void)
+static bool is_gre_gso(struct sk_buff *skb)
 {
-       int ret;
-
-       ret = gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
-       if (ret)
-               return ret;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
-       ret = inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
-       if (ret)
-               return ret;
-#endif
-       return 0;
+       return skb_is_gso(skb);
 }
 
-int gre_cisco_register(struct gre_cisco_protocol *newp)
+void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
+                     int hdr_len)
 {
-       int err;
-
-       err = gre_compat_init();
-       if (err)
-               return err;
-
-       return (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, NULL, newp) == NULL) ?
-               0 : -EBUSY;
-}
+       struct gre_base_hdr *greh;
 
-int gre_cisco_unregister(struct gre_cisco_protocol *proto)
-{
-       int ret;
+       __skb_push(skb, hdr_len);
 
-       ret = (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, proto, NULL) == proto) ?
-               0 : -EINVAL;
+       greh = (struct gre_base_hdr *)skb->data;
+       greh->flags = tnl_flags_to_gre_flags(tpi->flags);
+       greh->protocol = tpi->proto;
 
-       if (ret)
-               return ret;
+       if (tpi->flags & (TUNNEL_KEY | TUNNEL_CSUM | TUNNEL_SEQ)) {
+               __be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
 
-       synchronize_net();
-       ret = gre_compat_exit();
-       return ret;
+               if (tpi->flags & TUNNEL_SEQ) {
+                       *ptr = tpi->seq;
+                       ptr--;
+               }
+               if (tpi->flags & TUNNEL_KEY) {
+                       *ptr = tpi->key;
+                       ptr--;
+               }
+               if (tpi->flags & TUNNEL_CSUM && !is_gre_gso(skb)) {
+                       *ptr = 0;
+                       *(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0,
+                                               skb->len, 0));
+               }
+       }
 }
 
-#endif /* 3.11 */
-
 #endif /* CONFIG_NET_IPGRE_DEMUX */
diff --git a/datapath/linux/compat/hash-x86.c b/datapath/linux/compat/hash-x86.c
new file mode 100644 (file)
index 0000000..ca259b9
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Some portions derived from code covered by the following notice:
+ *
+ * Copyright (c) 2010-2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Intel Corporation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)
+
+#ifdef CONFIG_X86
+
+#include <linux/hash.h>
+
+#include <asm/processor.h>
+#include <asm/cpufeature.h>
+
+static inline u32 crc32_u32(u32 crc, u32 val)
+{
+       asm ("crc32l %1,%0\n" : "+r" (crc) : "rm" (val));
+       return crc;
+}
+
+static u32 intel_crc4_2_hash(const void *data, u32 len, u32 seed)
+{
+       const u32 *p32 = (const u32 *) data;
+       u32 i, tmp = 0;
+
+       for (i = 0; i < len / 4; i++)
+               seed = crc32_u32(*p32++, seed);
+
+       switch (3 - (len & 0x03)) {
+       case 0:
+               tmp |= *((const u8 *) p32 + 2) << 16;
+               /* fallthrough */
+       case 1:
+               tmp |= *((const u8 *) p32 + 1) << 8;
+               /* fallthrough */
+       case 2:
+               tmp |= *((const u8 *) p32);
+               seed = crc32_u32(tmp, seed);
+       default:
+               break;
+       }
+
+       return seed;
+}
+
+static u32 intel_crc4_2_hash2(const u32 *data, u32 len, u32 seed)
+{
+       const u32 *p32 = (const u32 *) data;
+       u32 i;
+
+       for (i = 0; i < len; i++)
+               seed = crc32_u32(*p32++, seed);
+
+       return seed;
+}
+
+void setup_arch_fast_hash(struct fast_hash_ops *ops)
+{
+       if (cpu_has_xmm4_2) {
+               ops->hash  = intel_crc4_2_hash;
+               ops->hash2 = intel_crc4_2_hash2;
+       }
+}
+
+#endif /* CONFIG_X86 */
+#endif /* < 3.14 */
diff --git a/datapath/linux/compat/hash.c b/datapath/linux/compat/hash.c
new file mode 100644 (file)
index 0000000..5e0d324
--- /dev/null
@@ -0,0 +1,51 @@
+/* General purpose hashing library
+ *
+ * That's a start of a kernel hashing library, which can be extended
+ * with further algorithms in future. arch_fast_hash{2,}() will
+ * eventually resolve to an architecture optimized implementation.
+ *
+ * Copyright 2013 Francesco Fusco <ffusco@redhat.com>
+ * Copyright 2013 Daniel Borkmann <dborkman@redhat.com>
+ * Copyright 2013 Thomas Graf <tgraf@redhat.com>
+ * Licensed under the GNU General Public License, version 2.0 (GPLv2)
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)
+
+#include <linux/cache.h>
+#include <linux/compiler.h>
+#include <linux/jhash.h>
+#include <linux/hash.h>
+#include <linux/kernel.h>
+
+static struct fast_hash_ops arch_hash_ops __read_mostly = {
+       .hash  = jhash,
+       .hash2 = jhash2,
+};
+
+static bool arch_inited __read_mostly;
+static void init_arch(void)
+{
+       if (likely(arch_inited))
+               return;
+
+       setup_arch_fast_hash(&arch_hash_ops);
+       arch_inited = true;
+}
+
+u32 arch_fast_hash(const void *data, u32 len, u32 seed)
+{
+       init_arch();
+
+       return arch_hash_ops.hash(data, len, seed);
+}
+
+u32 arch_fast_hash2(const u32 *data, u32 len, u32 seed)
+{
+       init_arch();
+
+       return arch_hash_ops.hash2(data, len, seed);
+}
+
+#endif /* < 3.14 */
diff --git a/datapath/linux/compat/include/asm/hash.h b/datapath/linux/compat/include/asm/hash.h
new file mode 100644 (file)
index 0000000..2ed5a9b
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef _ASM_HASH_WRAPPER_H
+#define _ASM_HASH_WRAPPER_H
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)
+#include_next <asm/hash.h>
+#else
+
+struct fast_hash_ops;
+#ifdef CONFIG_X86
+extern void setup_arch_fast_hash(struct fast_hash_ops *ops);
+#else
+static inline void setup_arch_fast_hash(struct fast_hash_ops *ops) { }
+#endif
+
+#endif /* < 3.14 */
+
+#endif /* _ASM_HASH_WRAPPER_H */
diff --git a/datapath/linux/compat/include/linux/bug.h b/datapath/linux/compat/include/linux/bug.h
new file mode 100644 (file)
index 0000000..6538a22
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __LINUX_BUG_WRAPPER_H
+#define __LINUX_BUG_WRAPPER_H 1
+
+#include_next <linux/bug.h>
+
+#ifdef __CHECKER__
+#ifndef BUILD_BUG_ON_INVALID
+#define  BUILD_BUG_ON_INVALID(e) (0)
+#endif
+
+#endif /* __CHECKER__ */
+
+#endif
diff --git a/datapath/linux/compat/include/linux/hash.h b/datapath/linux/compat/include/linux/hash.h
new file mode 100644 (file)
index 0000000..ac8531b
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef _LINUX_HASH_WRAPPER_H
+#define _LINUX_HASH_WRAPPER_H
+
+#include_next <linux/hash.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)
+#include <asm/hash.h>
+
+struct fast_hash_ops {
+       u32 (*hash)(const void *data, u32 len, u32 seed);
+       u32 (*hash2)(const u32 *data, u32 len, u32 seed);
+};
+
+/**
+ *     arch_fast_hash - Caclulates a hash over a given buffer that can have
+ *                      arbitrary size. This function will eventually use an
+ *                      architecture-optimized hashing implementation if
+ *                      available, and trades off distribution for speed.
+ *
+ *     @data: buffer to hash
+ *     @len: length of buffer in bytes
+ *     @seed: start seed
+ *
+ *     Returns 32bit hash.
+ */
+extern u32 arch_fast_hash(const void *data, u32 len, u32 seed);
+
+/**
+ *     arch_fast_hash2 - Caclulates a hash over a given buffer that has a
+ *                       size that is of a multiple of 32bit words. This
+ *                       function will eventually use an architecture-
+ *                       optimized hashing implementation if available,
+ *                       and trades off distribution for speed.
+ *
+ *     @data: buffer to hash (must be 32bit padded)
+ *     @len: number of 32bit words
+ *     @seed: start seed
+ *
+ *     Returns 32bit hash.
+ */
+extern u32 arch_fast_hash2(const u32 *data, u32 len, u32 seed);
+#endif /* < 3.14 */
+
+#endif /* _LINUX_HASH_WRAPPER_H */
index b303f39..e04f308 100644 (file)
@@ -60,12 +60,17 @@ static inline struct net_device *dev_get_by_index_rcu(struct net *net, int ifind
 #define NETIF_F_FSO 0
 #endif
 
+#ifndef HAVE_NETDEV_FEATURES_T
+typedef u32 netdev_features_t;
+#endif
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
 #define skb_gso_segment rpl_skb_gso_segment
-struct sk_buff *rpl_skb_gso_segment(struct sk_buff *skb, u32 features);
+struct sk_buff *rpl_skb_gso_segment(struct sk_buff *skb,
+                                    netdev_features_t features);
 
 #define netif_skb_features rpl_netif_skb_features
-u32 rpl_netif_skb_features(struct sk_buff *skb);
+netdev_features_t rpl_netif_skb_features(struct sk_buff *skb);
 
 #define netif_needs_gso rpl_netif_needs_gso
 static inline int rpl_netif_needs_gso(struct sk_buff *skb, int features)
@@ -75,10 +80,6 @@ static inline int rpl_netif_needs_gso(struct sk_buff *skb, int features)
 }
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
-typedef u32 netdev_features_t;
-#endif
-
 #ifndef HAVE___SKB_GSO_SEGMENT
 static inline struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
                                                netdev_features_t features,
index a1b689c..cd1e1a0 100644 (file)
@@ -3,7 +3,7 @@
 
 #include_next <linux/rtnetlink.h>
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)
+#ifndef HAVE_LOCKDEP_RTNL_IS_HELD
 #ifdef CONFIG_PROVE_LOCKING
 static inline int lockdep_rtnl_is_held(void)
 {
index 4f22600..de0c56a 100644 (file)
@@ -188,10 +188,40 @@ static inline bool skb_warn_if_lro(const struct sk_buff *skb)
 #endif
 
 #ifndef HAVE_SKB_FRAG_PAGE
+#include <linux/mm.h>
+
 static inline struct page *skb_frag_page(const skb_frag_t *frag)
 {
        return frag->page;
 }
+
+static inline void __skb_frag_set_page(skb_frag_t *frag, struct page *page)
+{
+       frag->page = page;
+}
+static inline void skb_frag_size_set(skb_frag_t *frag, unsigned int size)
+{
+       frag->size = size;
+}
+static inline void __skb_frag_ref(skb_frag_t *frag)
+{
+       get_page(skb_frag_page(frag));
+}
+static inline void __skb_frag_unref(skb_frag_t *frag)
+{
+       put_page(skb_frag_page(frag));
+}
+
+static inline void skb_frag_ref(struct sk_buff *skb, int f)
+{
+       __skb_frag_ref(&skb_shinfo(skb)->frags[f]);
+}
+
+static inline void skb_frag_unref(struct sk_buff *skb, int f)
+{
+       __skb_frag_unref(&skb_shinfo(skb)->frags[f]);
+}
+
 #endif
 
 #ifndef HAVE_SKB_RESET_MAC_LEN
@@ -228,4 +258,27 @@ static inline __u32 skb_get_rxhash(struct sk_buff *skb)
 }
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)
+unsigned int skb_zerocopy_headlen(const struct sk_buff *from);
+void skb_zerocopy(struct sk_buff *to, const struct sk_buff *from, int len, 
+                 int hlen);
+#endif
+
+
+#ifndef HAVE_SKB_HAS_FRAG_LIST
+#define skb_has_frag_list skb_has_frags
+#endif
+
+#ifndef HAVE___SKB_FILL_PAGE_DESC
+static inline void __skb_fill_page_desc(struct sk_buff *skb, int i,
+                                       struct page *page, int off, int size)
+{
+       skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+       __skb_frag_set_page(frag, page);
+       frag->page_offset       = off;
+       skb_frag_size_set(frag, size);
+}
+#endif
+
 #endif
index 91fb7af..a6f29c4 100644 (file)
@@ -4,22 +4,26 @@
 #include <linux/skbuff.h>
 #include <net/ip_tunnels.h>
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) || \
+   defined(HAVE_GRE_CISCO_REGISTER)
 #include_next <net/gre.h>
+#endif
 
-#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) */
+#ifndef HAVE_GRE_CISCO_REGISTER
 
-#define GREPROTO_CISCO         0
-#define GREPROTO_MAX           2
+/* GRE demux not available, implement our own demux. */
+#define MAX_GRE_PROTO_PRIORITY 255
 
-struct gre_protocol {
-       int  (*handler)(struct sk_buff *skb);
+struct gre_cisco_protocol {
+       int (*handler)(struct sk_buff *skb, const struct tnl_ptk_info *tpi);
+       u8 priority;
 };
 
-int gre_add_protocol(const struct gre_protocol *proto, u8 version);
-int gre_del_protocol(const struct gre_protocol *proto, u8 version);
+#define gre_cisco_register rpl_gre_cisco_register
+int gre_cisco_register(struct gre_cisco_protocol *proto);
 
-#endif
+#define gre_cisco_unregister rpl_gre_cisco_unregister
+int gre_cisco_unregister(struct gre_cisco_protocol *proto);
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
 struct gre_base_hdr {
@@ -72,24 +76,7 @@ static inline __be16 tnl_flags_to_gre_flags(__be16 tflags)
        return flags;
 }
 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
-/* GRE demux not available, implement our own demux. */
-#define MAX_GRE_PROTO_PRIORITY 255
-#define gre_cisco_protocol rpl_gre_cisco_protocol
-
-struct gre_cisco_protocol {
-       int (*handler)(struct sk_buff *skb, const struct tnl_ptk_info *tpi);
-       u8 priority;
-};
-
-#define gre_cisco_register rpl_gre_cisco_register
-int gre_cisco_register(struct gre_cisco_protocol *proto);
-
-#define gre_cisco_unregister rpl_gre_cisco_unregister
-int gre_cisco_unregister(struct gre_cisco_protocol *proto);
-
-#endif
+#endif /* HAVE_GRE_CISCO_REGISTER */
 
 #define gre_build_header rpl_gre_build_header
 void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
@@ -112,5 +99,4 @@ static inline int ip_gre_calc_hlen(__be16 o_flags)
        return addend;
 }
 
-
 #endif
index 3d28a9b..1dc5abf 100644 (file)
@@ -3,7 +3,7 @@
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
 #ifndef HAVE_CAN_CHECKSUM_PROTOCOL
-static bool can_checksum_protocol(unsigned long features, __be16 protocol)
+static bool can_checksum_protocol(netdev_features_t features, __be16 protocol)
 {
        return  ((features & NETIF_F_GEN_CSUM) ||
                ((features & NETIF_F_V4_CSUM) &&
@@ -31,7 +31,9 @@ static inline int illegal_highdma(struct net_device *dev, struct sk_buff *skb)
        return 0;
 }
 
-static u32 harmonize_features(struct sk_buff *skb, __be16 protocol, u32 features)
+static netdev_features_t harmonize_features(struct sk_buff *skb,
+                                           __be16 protocol,
+                                           netdev_features_t features)
 {
        if (!can_checksum_protocol(features, protocol)) {
                features &= ~NETIF_F_ALL_CSUM;
@@ -43,12 +45,12 @@ static u32 harmonize_features(struct sk_buff *skb, __be16 protocol, u32 features
        return features;
 }
 
-u32 rpl_netif_skb_features(struct sk_buff *skb)
+netdev_features_t rpl_netif_skb_features(struct sk_buff *skb)
 {
        unsigned long vlan_features = skb->dev->vlan_features;
 
        __be16 protocol = skb->protocol;
-       u32 features = skb->dev->features;
+       netdev_features_t features = skb->dev->features;
 
        if (protocol == htons(ETH_P_8021Q)) {
                struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data;
@@ -68,7 +70,8 @@ u32 rpl_netif_skb_features(struct sk_buff *skb)
        }
 }
 
-struct sk_buff *rpl_skb_gso_segment(struct sk_buff *skb, u32 features)
+struct sk_buff *rpl_skb_gso_segment(struct sk_buff *skb,
+                                   netdev_features_t features)
 {
        int vlan_depth = ETH_HLEN;
        __be16 type = skb->protocol;
index 3baa09e..ddd7bc8 100644 (file)
@@ -14,3 +14,98 @@ void __skb_warn_lro_forwarding(const struct sk_buff *skb)
 }
 
 #endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)
+
+static inline bool head_frag(const struct sk_buff *skb)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+       return skb->head_frag;
+#else
+       return false;
+#endif
+}
+
+ /**
+ *     skb_zerocopy_headlen - Calculate headroom needed for skb_zerocopy()
+ *     @from: source buffer
+ *
+ *     Calculates the amount of linear headroom needed in the 'to' skb passed
+ *     into skb_zerocopy().
+ */
+unsigned int
+skb_zerocopy_headlen(const struct sk_buff *from)
+{
+       unsigned int hlen = 0;
+
+       if (!head_frag(from) ||
+           skb_headlen(from) < L1_CACHE_BYTES ||
+           skb_shinfo(from)->nr_frags >= MAX_SKB_FRAGS)
+               hlen = skb_headlen(from);
+
+       if (skb_has_frag_list(from))
+               hlen = from->len;
+
+       return hlen;
+}
+
+/**
+ *     skb_zerocopy - Zero copy skb to skb
+ *     @to: destination buffer
+ *     @source: source buffer
+ *     @len: number of bytes to copy from source buffer
+ *     @hlen: size of linear headroom in destination buffer
+ *
+ *     Copies up to `len` bytes from `from` to `to` by creating references
+ *     to the frags in the source buffer.
+ *
+ *     The `hlen` as calculated by skb_zerocopy_headlen() specifies the
+ *     headroom in the `to` buffer.
+ */
+void
+skb_zerocopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen)
+{
+       int i, j = 0;
+       int plen = 0; /* length of skb->head fragment */
+       struct page *page;
+       unsigned int offset;
+
+       BUG_ON(!head_frag(from) && !hlen);
+
+       /* dont bother with small payloads */
+       if (len <= skb_tailroom(to)) {
+               skb_copy_bits(from, 0, skb_put(to, len), len);
+               return;
+       }
+
+       if (hlen) {
+               skb_copy_bits(from, 0, skb_put(to, hlen), hlen);
+               len -= hlen;
+       } else {
+               plen = min_t(int, skb_headlen(from), len);
+               if (plen) {
+                       page = virt_to_head_page(from->head);
+                       offset = from->data - (unsigned char *)page_address(page);
+                       __skb_fill_page_desc(to, 0, page, offset, plen);
+                       get_page(page);
+                       j = 1;
+                       len -= plen;
+               }
+       }
+
+       to->truesize += len + plen;
+       to->len += len + plen;
+       to->data_len += len + plen;
+
+       for (i = 0; i < skb_shinfo(from)->nr_frags; i++) {
+               if (!len)
+                       break;
+               skb_shinfo(to)->frags[j] = skb_shinfo(from)->frags[i];
+               skb_shinfo(to)->frags[j].size = min_t(int, skb_shinfo(to)->frags[j].size, len);
+               len -= skb_shinfo(to)->frags[j].size;
+               skb_frag_ref(to, j);
+               j++;
+       }
+       skb_shinfo(to)->nr_frags = j;
+}
+#endif
index 2882cc7..7f12acc 100644 (file)
@@ -35,6 +35,9 @@
 #include "vport.h"
 #include "vport-internal_dev.h"
 
+static void ovs_vport_record_error(struct vport *,
+                                  enum vport_err_type err_type);
+
 /* List of statically compiled vport implementations.  Don't forget to also
  * add yours to the list at the bottom of vport.h. */
 static const struct vport_ops *vport_ops_list[] = {
@@ -411,7 +414,8 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb)
  * If using the vport generic stats layer indicate that an error of the given
  * type has occurred.
  */
-void ovs_vport_record_error(struct vport *vport, enum vport_err_type err_type)
+static void ovs_vport_record_error(struct vport *vport,
+                                  enum vport_err_type err_type)
 {
        spin_lock(&vport->stats_lock);
 
index 995889c..2cf2b18 100644 (file)
@@ -193,7 +193,6 @@ static inline struct vport *vport_from_priv(const void *priv)
 
 void ovs_vport_receive(struct vport *, struct sk_buff *,
                       struct ovs_key_ipv4_tunnel *);
-void ovs_vport_record_error(struct vport *, enum vport_err_type err_type);
 
 /* List of statically compiled vport implementations.  Don't forget to also
  * add yours to the list at the top of vport.c. */
index be1a5a5..3a1056f 100644 (file)
@@ -1,13 +1,77 @@
-openvswitch (2.0.90-1) unstable; urgency=low
+openvswitch (2.1.90-1) unstable; urgency=low
    [ Open vSwitch team ]
+   * New upstream version
+    - Nothing yet!  Try NEWS...
+
+ -- Open vSwitch team <dev@openvswitch.org>  Mon, 23 Dec 2013 18:07:18 -0700
+
+openvswitch (2.1.0-1) unstable; urgency=low
+   [ Open vSwitch team ]
+   * New upstream version
+   - Address prefix tracking support for flow tables.  New columns
+     "prefixes" in OVS-DB table "Flow_Table" controls which packet
+     header fields are used for address prefix tracking.  Prefix
+     tracking allows the classifier to skip rules with longer than
+     necessary prefixes, resulting in better wildcarding for datapath
+     flows.  Default configuration is to not use any fields for prefix
+     tracking.  However, if any flow tables contain both exact matches
+     and masked matches for IP address fields, OVS performance may be
+     increased by using this feature.
+     * As of now, the fields for which prefix lookup can be enabled
+       are: 'tun_id', 'tun_src', 'tun_dst', 'nw_src', 'nw_dst' (or
+       aliases 'ip_src' and 'ip_dst'), 'ipv6_src', and 'ipv6_dst'.
+       (Using this feature for 'tun_id' would only make sense if the
+       tunnel IDs have prefix structure similar to IP addresses.)
+     * There is a maximum number of fields that can be enabled for any
+       one flow table.  Currently this limit is 3.
+     * Examples:
+       $ ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- \
+         --id=@N1 create Flow_Table name=table0
+       $ ovs-vsctl set Bridge br0 flow_tables:1=@N1 -- \
+         --id=@N1 create Flow_Table name=table1
+       $ ovs-vsctl set Flow_Table table0 prefixes=ip_dst,ip_src
+       $ ovs-vsctl set Flow_Table table1 prefixes=[]
+   - TCP flags matching: OVS now supports matching of TCP flags.  This
+     has an adverse performance impact when using OVS userspace 1.10
+     or older (no megaflows support) together with the new OVS kernel
+     module.  It is recommended that the kernel and userspace modules
+     both are upgraded at the same time.
+   - The default OpenFlow and OVSDB ports will change to
+     IANA-assigned numbers in a future release.  Consider updating
+     your installations to specify port numbers instead of using the
+     defaults.
+   - OpenFlow:
+     * The OpenFlow 1.1+ "Write-Actions" instruction is now supported.
+     * OVS limits the OpenFlow port numbers it assigns to port 32767 and
+       below, leaving port numbers above that range free for assignment
+       by the controller.
+     * ovs-vswitchd now honors changes to the "ofport_request" column
+       in the Interface table by changing the port's OpenFlow port
+       number.
+   - ovs-vswitchd.conf.db.5 man page will contain graphviz/dot
+     diagram only if graphviz package was installed at the build time.
+   - Support for Linux kernels up to 3.11
+   - ovs-dpctl:
+     The "show" command also displays mega flow mask stats.
+   - ovs-ofctl:
+     * New command "ofp-parse-pcap" to dump OpenFlow from PCAP files.
+   - ovs-controller has been renamed test-controller.  It is no longer
+     packaged or installed by default, because too many users assumed
+     incorrectly that ovs-controller was a necessary or desirable part
+     of an Open vSwitch deployment.
+   - Added vlog option to export to a UDP syslog sink.
+   - ovsdb-client:
+     * The "monitor" command can now monitor all tables in a database,
+       instead of being limited to a single table.
+   - The flow-eviction-threshold has been replaced by the flow-limit which is a
+     hard limit on the number of flows in the datapath.  It defaults to 200,000
+     flows.  OVS automatically adjusts this number depending on network
+     conditions.
    * The openvswitch-controller package has been removed, because too many
      users assumed incorrectly that ovs-controller was a necessary or
      desirable part of an Open vSwitch deployment.
-   * New upstream version
-    - Try NEWS for more details...
-
- -- Open vSwitch team <dev@openvswitch.org>  Wed, 28 Aug 2013 16:17:38 -0700
 
+ -- Open vSwitch team <dev@openvswitch.org>  Mon, 23 Dec 2013 18:07:18 -0700
 
 openvswitch (2.0.0-1) unstable; urgency=low
    [ Open vSwitch team ]
index 986f7a1..0676387 100644 (file)
@@ -8,7 +8,7 @@ Upstream Authors (from AUTHORS):
 
 Upstream Copyright Holders:
 
-       Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+       Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
        Copyright (c) 2010 Jean Tourrilhes - HP-Labs.
        Copyright (c) 2008,2009,2010 Citrix Systems, Inc.
        and authors listed above.
@@ -182,6 +182,14 @@ License:
     .\" Copyright (C) 2007, 2009, 2011 Free Software Foundation, Inc.
     .\" You may freely use, modify and/or distribute this file.
 
+* m4/absolute-header.m4, by Derek Price, and m4/include_next.m4, by
+  Paul Eggert and Derek Price bear the following notices:
+
+    Copyright (C) 2006-2013 Free Software Foundation, Inc.
+    This file is free software; the Free Software Foundation
+    gives unlimited permission to copy and/or distribute it,
+    with or without modifications, as long as this notice is preserved.
+
 * All other components of this package are licensed under
   The Apache License Version 2.0.
 
index f34e5c9..37a6f77 100644 (file)
@@ -2,3 +2,4 @@ include include/linux/automake.mk
 include include/openflow/automake.mk
 include include/openvswitch/automake.mk
 include include/sparse/automake.mk
+include include/windows/automake.mk
index b429201..5137c2f 100644 (file)
@@ -60,7 +60,15 @@ struct ovs_header {
 
 #define OVS_DATAPATH_FAMILY  "ovs_datapath"
 #define OVS_DATAPATH_MCGROUP "ovs_datapath"
-#define OVS_DATAPATH_VERSION 0x1
+
+/* V2:
+ *   - API users are expected to provide OVS_DP_ATTR_USER_FEATURES
+ *     when creating the datapath.
+ */
+#define OVS_DATAPATH_VERSION 2
+
+/* First OVS datapath version to support features */
+#define OVS_DP_VER_FEATURES 2
 
 enum ovs_datapath_cmd {
        OVS_DP_CMD_UNSPEC,
@@ -95,6 +103,7 @@ enum ovs_datapath_attr {
        OVS_DP_ATTR_UPCALL_PID,         /* Netlink PID to receive upcalls */
        OVS_DP_ATTR_STATS,              /* struct ovs_dp_stats */
        OVS_DP_ATTR_MEGAFLOW_STATS,     /* struct ovs_dp_megaflow_stats */
+       OVS_DP_ATTR_USER_FEATURES,      /* OVS_DP_F_*  */
        __OVS_DP_ATTR_MAX
 };
 
@@ -126,6 +135,9 @@ struct ovs_vport_stats {
        __u64   tx_dropped;             /* no space available in linux  */
 };
 
+/* Allow last Netlink attribute to be unaligned */
+#define OVS_DP_F_UNALIGNED     (1 << 0)
+
 /* Fixed logical ports. */
 #define OVSP_LOCAL      ((__u32)0)
 
diff --git a/include/windows/automake.mk b/include/windows/automake.mk
new file mode 100644 (file)
index 0000000..07ada1b
--- /dev/null
@@ -0,0 +1,10 @@
+# Copyright (C) 2013 Nicira, Inc.
+#
+# Copying and distribution of this file, with or without modification
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.  This file is offered as-is,
+# without warranty of any kind.
+
+noinst_HEADERS += \
+       include/windows/windefs.h \
+       include/windows/syslog.h
diff --git a/include/windows/syslog.h b/include/windows/syslog.h
new file mode 100644 (file)
index 0000000..484cd10
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013 Cloudbase Solutions Srl
+ *
+ * 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 SYSLOG_H
+#define SYSLOG_H 1
+
+#define LOG_EMERG       0       /* system is unusable */
+#define LOG_ALERT       1       /* action must be taken immediately */
+#define LOG_CRIT        2       /* critical conditions */
+#define LOG_ERR         3       /* error conditions */
+#define LOG_WARNING     4       /* warning conditions */
+#define LOG_NOTICE      5       /* normal but significant condition */
+#define LOG_INFO        6       /* informational */
+#define LOG_DEBUG       7       /* debug-level messages */
+#define LOG_NDELAY      8       /* don't delay open */
+#define LOG_DAEMON      24      /* system daemons */
+
+#endif /* syslog.h */
diff --git a/include/windows/windefs.h b/include/windows/windefs.h
new file mode 100644 (file)
index 0000000..dcfa20b
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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 WINDEFS_H
+#define WINDEFS_H 1
+
+#include <Winsock2.h>
+#include <In6addr.h>
+#include <WS2tcpip.h>
+#include <windows.h>
+#include <BaseTsd.h>
+
+#define inline __inline
+#define __func__ __FUNCTION__
+#define ssize_t SSIZE_T
+#define u_int8_t uint8_t
+#define u_int16_t uint16_t
+#define u_int32_t uint32_t
+#define u_int64_t uint64_t
+
+#endif /* windefs.h */
index 31346e4..2d07244 100644 (file)
@@ -5,6 +5,8 @@
 /coverage-counters.c
 /ofp-errors.inc
 /ofp-msgs.inc
+/stdio.h
+/string.h
 /vswitch-idl.c
 /vswitch-idl.h
 /vswitch-idl.ovsidl
index 3eef26e..217997b 100644 (file)
@@ -41,11 +41,11 @@ void
 async_append_write(struct async_append *ap OVS_UNUSED,
                    const void *data OVS_UNUSED, size_t size OVS_UNUSED)
 {
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 void
 async_append_flush(struct async_append *ap OVS_UNUSED)
 {
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
index 75608a8..5e90f9f 100644 (file)
@@ -5,9 +5,12 @@
 # notice and this notice are preserved.  This file is offered as-is,
 # without warranty of any kind.
 
-noinst_LIBRARIES += lib/libopenvswitch.a
+lib_LTLIBRARIES += lib/libopenvswitch.la
 
-lib_libopenvswitch_a_SOURCES = \
+lib_libopenvswitch_la_LIBADD = $(SSL_LIBS)
+lib_libopenvswitch_la_LDFLAGS = -release $(VERSION)
+
+lib_libopenvswitch_la_SOURCES = \
        lib/aes128.c \
        lib/aes128.h \
        lib/async-append.h \
@@ -29,6 +32,8 @@ lib_libopenvswitch_a_SOURCES = \
        lib/command-line.c \
        lib/command-line.h \
        lib/compiler.h \
+       lib/connectivity.c \
+       lib/connectivity.h \
        lib/coverage.c \
        lib/coverage.h \
        lib/crc32c.c \
@@ -193,8 +198,8 @@ lib_libopenvswitch_a_SOURCES = \
        lib/stream-unix.c \
        lib/stream.c \
        lib/stream.h \
+       lib/stdio.c \
        lib/string.c \
-       lib/string.h \
        lib/svec.c \
        lib/svec.h \
        lib/table.c \
@@ -234,29 +239,34 @@ lib_libopenvswitch_a_SOURCES = \
        lib/vswitch-idl.h \
        lib/vtep-idl.c \
        lib/vtep-idl.h
+EXTRA_DIST += \
+       lib/stdio.h.in \
+       lib/string.h.in
 
-nodist_lib_libopenvswitch_a_SOURCES = \
+nodist_lib_libopenvswitch_la_SOURCES = \
        lib/dirs.c
-CLEANFILES += $(nodist_lib_libopenvswitch_a_SOURCES)
+CLEANFILES += $(nodist_lib_libopenvswitch_la_SOURCES)
 
-noinst_LIBRARIES += lib/libsflow.a
-lib_libsflow_a_SOURCES = \
+lib_LTLIBRARIES += lib/libsflow.la
+lib_libsflow_la_LDFLAGS = -release $(VERSION)
+lib_libsflow_la_SOURCES = \
        lib/sflow_api.h \
        lib/sflow.h \
        lib/sflow_agent.c \
        lib/sflow_sampler.c \
        lib/sflow_poller.c \
        lib/sflow_receiver.c
-lib_libsflow_a_CFLAGS = $(AM_CFLAGS)
+lib_libsflow_la_CPPFLAGS = $(AM_CPPFLAGS)
+lib_libsflow_la_CFLAGS = $(AM_CFLAGS)
 if HAVE_WNO_UNUSED
-lib_libsflow_a_CFLAGS += -Wno-unused
+lib_libsflow_la_CFLAGS += -Wno-unused
 endif
 if HAVE_WNO_UNUSED_PARAMETER
-lib_libsflow_a_CFLAGS += -Wno-unused-parameter
+lib_libsflow_la_CFLAGS += -Wno-unused-parameter
 endif
 
 if LINUX_DATAPATH
-lib_libopenvswitch_a_SOURCES += \
+lib_libopenvswitch_la_SOURCES += \
        lib/dpif-linux.c \
        lib/dpif-linux.h \
        lib/netdev-linux.c \
@@ -273,18 +283,18 @@ lib_libopenvswitch_a_SOURCES += \
 endif
 
 if HAVE_POSIX_AIO
-lib_libopenvswitch_a_SOURCES += lib/async-append-aio.c
+lib_libopenvswitch_la_SOURCES += lib/async-append-aio.c
 else
-lib_libopenvswitch_a_SOURCES += lib/async-append-null.c
+lib_libopenvswitch_la_SOURCES += lib/async-append-null.c
 endif
 
 if ESX
-lib_libopenvswitch_a_SOURCES += \
+lib_libopenvswitch_la_SOURCES += \
         lib/route-table-stub.c
 endif
 
 if HAVE_IF_DL
-lib_libopenvswitch_a_SOURCES += \
+lib_libopenvswitch_la_SOURCES += \
        lib/netdev-bsd.c \
        lib/rtbsd.c \
        lib/rtbsd.h \
@@ -292,8 +302,8 @@ lib_libopenvswitch_a_SOURCES += \
 endif
 
 if HAVE_OPENSSL
-lib_libopenvswitch_a_SOURCES += lib/stream-ssl.c
-nodist_lib_libopenvswitch_a_SOURCES += lib/dhparams.c
+lib_libopenvswitch_la_SOURCES += lib/stream-ssl.c
+nodist_lib_libopenvswitch_la_SOURCES += lib/dhparams.c
 lib/dhparams.c: lib/dh1024.pem lib/dh2048.pem lib/dh4096.pem
        (echo '#include "lib/dhparams.h"' &&                            \
         openssl dhparam -C -in $(srcdir)/lib/dh1024.pem -noout &&      \
@@ -302,7 +312,7 @@ lib/dhparams.c: lib/dh1024.pem lib/dh2048.pem lib/dh4096.pem
        | sed 's/\(get_dh[0-9]*\)()/\1(void)/' > lib/dhparams.c.tmp
        mv lib/dhparams.c.tmp lib/dhparams.c
 else
-lib_libopenvswitch_a_SOURCES += lib/stream-nossl.c
+lib_libopenvswitch_la_SOURCES += lib/stream-nossl.c
 endif
 
 EXTRA_DIST += \
@@ -395,25 +405,3 @@ lib-install-data-local:
        $(MKDIR_P) $(DESTDIR)$(LOGDIR)
        $(MKDIR_P) $(DESTDIR)$(DBDIR)
 
-if !USE_LINKER_SECTIONS
-# All distributed sources, with names adjust properly for referencing
-# from $(builddir).
-all_sources = \
-       `for file in $(DIST_SOURCES); do \
-               if test -f $$file; then \
-                       echo $$file; \
-               else \
-                       echo $(VPATH)/$$file; \
-               fi; \
-        done`
-
-lib/coverage.$(OBJEXT): lib/coverage.def
-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/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 > $@
-CLEANFILES += lib/vlog-modules.def
-endif
index 1df5acd..b8574d5 100644 (file)
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -21,6 +21,7 @@
 #include <netinet/ip.h>
 
 #include "byte-order.h"
+#include "connectivity.h"
 #include "csum.h"
 #include "dpif.h"
 #include "dynamic-string.h"
@@ -37,6 +38,7 @@
 #include "packets.h"
 #include "poll-loop.h"
 #include "random.h"
+#include "seq.h"
 #include "smap.h"
 #include "timeval.h"
 #include "unaligned.h"
@@ -505,8 +507,8 @@ bfd_run(struct bfd *bfd) OVS_EXCLUDED(mutex)
 
     if (bfd->state > STATE_DOWN && now >= bfd->detect_time) {
         bfd_set_state(bfd, STATE_DOWN, DIAG_EXPIRED);
-        bfd_forwarding__(bfd);
     }
+    bfd_forwarding__(bfd);
 
     /* Decay may only happen when state is STATE_UP, bfd->decay_min_rx is
      * configured, and decay_detect_time is reached. */
@@ -733,6 +735,10 @@ bfd_process_packet(struct bfd *bfd, const struct flow *flow,
         goto out;
     }
 
+    if (bfd->rmt_state != rmt_state) {
+        seq_change(connectivity_seq_get());
+    }
+
     bfd->rmt_disc = ntohl(msg->my_disc);
     bfd->rmt_state = rmt_state;
     bfd->rmt_flags = flags;
@@ -758,7 +764,9 @@ bfd_process_packet(struct bfd *bfd, const struct flow *flow,
     rmt_min_rx = MAX(ntohl(msg->min_rx) / 1000, 1);
     if (bfd->rmt_min_rx != rmt_min_rx) {
         bfd->rmt_min_rx = rmt_min_rx;
-        bfd_set_next_tx(bfd);
+        if (bfd->next_tx) {
+            bfd_set_next_tx(bfd);
+        }
         log_msg(VLL_INFO, msg, "New remote min_rx", bfd);
     }
 
@@ -796,7 +804,7 @@ bfd_process_packet(struct bfd *bfd, const struct flow *flow,
             break;
         case STATE_ADMIN_DOWN:
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
     /* XXX: RFC 5880 Section 6.8.6 Demand mode related calculations here. */
@@ -851,6 +859,7 @@ bfd_forwarding__(struct bfd *bfd) OVS_REQUIRES(mutex)
                             && bfd->rmt_diag != DIAG_RCPATH_DOWN;
     if (bfd->last_forwarding != last_forwarding) {
         bfd->flap_count++;
+        seq_change(connectivity_seq_get());
     }
     return bfd->last_forwarding;
 }
@@ -1052,6 +1061,8 @@ bfd_set_state(struct bfd *bfd, enum state state, enum diag diag)
         if (bfd->state == STATE_UP && bfd->decay_min_rx) {
             bfd_decay_update(bfd);
         }
+
+        seq_change(connectivity_seq_get());
     }
 }
 
index ac568e9..51ad5bf 100644 (file)
@@ -17,6 +17,7 @@
 #include <config.h>
 #include "bitmap.h"
 #include <string.h>
+#include "util.h"
 
 /* Allocates and returns a bitmap initialized to all-1-bits. */
 unsigned long *
@@ -92,3 +93,18 @@ bitmap_scan(const unsigned long int *bitmap, size_t start, size_t end)
     }
     return i;
 }
+
+/* Returns the number of 1-bits in the 'n'-bit bitmap at 'bitmap'. */
+size_t
+bitmap_count1(const unsigned long int *bitmap, size_t n)
+{
+    size_t i;
+    size_t count = 0;
+
+    BUILD_ASSERT(ULONG_MAX <= UINT64_MAX);
+    for (i = 0; i < BITMAP_N_LONGS(n); i++) {
+        count += count_1bits(bitmap[i]);
+    }
+
+    return count;
+}
index 645f15f..5e6f8ed 100644 (file)
@@ -101,6 +101,7 @@ void bitmap_set_multiple(unsigned long *, size_t start, size_t count,
                          bool value);
 bool bitmap_equal(const unsigned long *, const unsigned long *, size_t n);
 size_t bitmap_scan(const unsigned long int *, size_t start, size_t end);
+size_t bitmap_count1(const unsigned long *, size_t n);
 
 #define BITMAP_FOR_EACH_1(IDX, SIZE, BITMAP) \
     for ((IDX) = bitmap_scan(BITMAP, 0, SIZE); (IDX) < (SIZE); \
index 9aec8c9..4a40a6a 100644 (file)
@@ -99,7 +99,7 @@ bundle_execute(const struct ofpact_bundle *bundle,
         return execute_ab(bundle, slave_enabled, aux);
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
index 9c65b34..5ee94d5 100644 (file)
--- a/lib/cfm.c
+++ b/lib/cfm.c
@@ -22,6 +22,7 @@
 #include <string.h>
 
 #include "byte-order.h"
+#include "connectivity.h"
 #include "dynamic-string.h"
 #include "flow.h"
 #include "hash.h"
@@ -31,6 +32,7 @@
 #include "packets.h"
 #include "poll-loop.h"
 #include "random.h"
+#include "seq.h"
 #include "timer.h"
 #include "timeval.h"
 #include "unixctl.h"
@@ -234,7 +236,7 @@ static int
 ccm_interval_to_ms(uint8_t interval)
 {
     switch (interval) {
-    case 0:  NOT_REACHED(); /* Explicitly not supported by 802.1ag. */
+    case 0:  OVS_NOT_REACHED(); /* Explicitly not supported by 802.1ag. */
     case 1:  return 3;      /* Not recommended due to timer resolution. */
     case 2:  return 10;     /* Not recommended due to timer resolution. */
     case 3:  return 100;
@@ -242,10 +244,10 @@ ccm_interval_to_ms(uint8_t interval)
     case 5:  return 10000;
     case 6:  return 60000;
     case 7:  return 600000;
-    default: NOT_REACHED(); /* Explicitly not supported by 802.1ag. */
+    default: OVS_NOT_REACHED(); /* Explicitly not supported by 802.1ag. */
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 static long long int
@@ -396,6 +398,7 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex)
         long long int interval = cfm_fault_interval(cfm);
         struct remote_mp *rmp, *rmp_next;
         bool old_cfm_fault = cfm->fault;
+        bool old_rmp_opup = cfm->remote_opup;
         bool demand_override;
         bool rmp_set_opup = false;
         bool rmp_set_opdown = false;
@@ -420,6 +423,7 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex)
                 cfm->health = 0;
             } else {
                 int exp_ccm_recvd;
+                int old_health = cfm->health;
 
                 rmp = CONTAINER_OF(hmap_first(&cfm->remote_mps),
                                    struct remote_mp, node);
@@ -434,6 +438,10 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex)
                 cfm->health = MIN(cfm->health, 100);
                 rmp->num_health_ccm = 0;
                 ovs_assert(cfm->health >= 0 && cfm->health <= 100);
+
+                if (cfm->health != old_health) {
+                    seq_change(connectivity_seq_get());
+                }
             }
             cfm->health_interval = 0;
         }
@@ -476,6 +484,10 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex)
             cfm->remote_opup = true;
         }
 
+        if (old_rmp_opup != cfm->remote_opup) {
+            seq_change(connectivity_seq_get());
+        }
+
         if (hmap_is_empty(&cfm->remote_mps)) {
             cfm->fault |= CFM_FAULT_RECV;
         }
@@ -497,6 +509,8 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex)
             if (old_cfm_fault == false || cfm->fault == false) {
                 cfm->flap_count++;
             }
+
+            seq_change(connectivity_seq_get());
         }
 
         cfm->booted = true;
@@ -1020,6 +1034,7 @@ cfm_unixctl_set_fault(struct unixctl_conn *conn, int argc, const char *argv[],
         }
     }
 
+    seq_change(connectivity_seq_get());
     unixctl_command_reply(conn, "OK");
 
 out:
index 33ade96..1675283 100644 (file)
 #include "hash.h"
 #include "odp-util.h"
 #include "ofp-util.h"
-#include "packets.h"
 #include "ovs-thread.h"
+#include "packets.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(classifier);
 
+struct trie_ctx;
 static struct cls_subtable *find_subtable(const struct classifier *,
                                           const struct minimask *);
 static struct cls_subtable *insert_subtable(struct classifier *,
@@ -42,7 +46,8 @@ static void update_subtables_after_removal(struct classifier *,
                                            unsigned int del_priority);
 
 static struct cls_rule *find_match_wc(const struct cls_subtable *,
-                                      const struct flow *,
+                                      const struct flow *, struct trie_ctx *,
+                                      unsigned int n_tries,
                                       struct flow_wildcards *);
 static struct cls_rule *find_equal(struct cls_subtable *,
                                    const struct miniflow *, uint32_t hash);
@@ -59,6 +64,21 @@ static struct cls_rule *insert_rule(struct classifier *,
 
 static struct cls_rule *next_rule_in_list__(struct cls_rule *);
 static struct cls_rule *next_rule_in_list(struct cls_rule *);
+
+static unsigned int minimask_get_prefix_len(const struct minimask *,
+                                            const struct mf_field *);
+static void trie_init(struct classifier *, int trie_idx,
+                      const struct mf_field *);
+static unsigned int trie_lookup(const struct cls_trie *, const struct flow *,
+                                unsigned int *checkbits);
+
+static void trie_destroy(struct trie_node *);
+static void trie_insert(struct cls_trie *, const struct cls_rule *, int mlen);
+static void trie_remove(struct cls_trie *, const struct cls_rule *, int mlen);
+static void mask_set_prefix_bits(struct flow_wildcards *, uint8_t be32ofs,
+                                 unsigned int nbits);
+static bool mask_prefix_bits_set(const struct flow_wildcards *,
+                                 uint8_t be32ofs, unsigned int nbits);
 \f
 /* cls_rule. */
 
@@ -164,6 +184,7 @@ classifier_init(struct classifier *cls, const uint8_t *flow_segments)
             cls->flow_segments[cls->n_flow_segments++] = *flow_segments++;
         }
     }
+    cls->n_tries = 0;
 }
 
 /* Destroys 'cls'.  Rules within 'cls', if any, are not freed; this is the
@@ -174,6 +195,11 @@ classifier_destroy(struct classifier *cls)
     if (cls) {
         struct cls_subtable *partition, *next_partition;
         struct cls_subtable *subtable, *next_subtable;
+        int i;
+
+        for (i = 0; i < cls->n_tries; i++) {
+            trie_destroy(cls->tries[i].root);
+        }
 
         HMAP_FOR_EACH_SAFE (subtable, next_subtable, hmap_node,
                             &cls->subtables) {
@@ -191,6 +217,83 @@ classifier_destroy(struct classifier *cls)
     }
 }
 
+/* We use uint64_t as a set for the fields below. */
+BUILD_ASSERT_DECL(MFF_N_IDS <= 64);
+
+/* Set the fields for which prefix lookup should be performed. */
+void
+classifier_set_prefix_fields(struct classifier *cls,
+                             const enum mf_field_id *trie_fields,
+                             unsigned int n_fields)
+{
+    uint64_t fields = 0;
+    int i, trie;
+
+    for (i = 0, trie = 0; i < n_fields && trie < CLS_MAX_TRIES; i++) {
+        const struct mf_field *field = mf_from_id(trie_fields[i]);
+        if (field->flow_be32ofs < 0 || field->n_bits % 32) {
+            /* Incompatible field.  This is the only place where we
+             * enforce these requirements, but the rest of the trie code
+             * depends on the flow_be32ofs to be non-negative and the
+             * field length to be a multiple of 32 bits. */
+            continue;
+        }
+
+        if (fields & (UINT64_C(1) << trie_fields[i])) {
+            /* Duplicate field, there is no need to build more than
+             * one index for any one field. */
+            continue;
+        }
+        fields |= UINT64_C(1) << trie_fields[i];
+
+        if (trie >= cls->n_tries || field != cls->tries[trie].field) {
+            trie_init(cls, trie, field);
+        }
+        trie++;
+    }
+
+    /* Destroy the rest. */
+    for (i = trie; i < cls->n_tries; i++) {
+        trie_init(cls, i, NULL);
+    }
+    cls->n_tries = trie;
+}
+
+static void
+trie_init(struct classifier *cls, int trie_idx,
+          const struct mf_field *field)
+{
+    struct cls_trie *trie = &cls->tries[trie_idx];
+    struct cls_subtable *subtable;
+
+    if (trie_idx < cls->n_tries) {
+        trie_destroy(trie->root);
+    }
+    trie->root = NULL;
+    trie->field = field;
+
+    /* Add existing rules to the trie. */
+    LIST_FOR_EACH (subtable, list_node, &cls->subtables_priority) {
+        unsigned int plen;
+
+        plen = field ? minimask_get_prefix_len(&subtable->mask, field) : 0;
+        /* Initialize subtable's prefix length on this field. */
+        subtable->trie_plen[trie_idx] = plen;
+
+        if (plen) {
+            struct cls_rule *head;
+
+            HMAP_FOR_EACH (head, hmap_node, &subtable->rules) {
+                struct cls_rule *rule;
+
+                FOR_EACH_RULE_IN_LIST (rule, head) {
+                    trie_insert(trie, rule, plen);
+                }
+            }
+        }
+    }
+}
+
 /* Returns true if 'cls' contains no classification rules, false otherwise. */
 bool
 classifier_is_empty(const struct classifier *cls)
@@ -269,6 +372,8 @@ classifier_replace(struct classifier *cls, struct cls_rule *rule)
 
     old_rule = insert_rule(cls, subtable, rule);
     if (!old_rule) {
+        int i;
+
         if (minimask_get_metadata_mask(&rule->match.mask) == OVS_BE64_MAX) {
             ovs_be64 metadata = miniflow_get_metadata(&rule->match.flow);
             rule->partition = create_partition(cls, subtable, metadata);
@@ -278,6 +383,12 @@ classifier_replace(struct classifier *cls, struct cls_rule *rule)
 
         subtable->n_rules++;
         cls->n_rules++;
+
+        for (i = 0; i < cls->n_tries; i++) {
+            if (subtable->trie_plen[i]) {
+                trie_insert(&cls->tries[i], rule, subtable->trie_plen[i]);
+            }
+        }
     } else {
         rule->partition = old_rule->partition;
     }
@@ -310,6 +421,12 @@ classifier_remove(struct classifier *cls, struct cls_rule *rule)
 
     subtable = find_subtable(cls, &rule->match.mask);
 
+    for (i = 0; i < cls->n_tries; i++) {
+        if (subtable->trie_plen[i]) {
+            trie_remove(&cls->tries[i], rule, subtable->trie_plen[i]);
+        }
+    }
+
     /* Remove rule node from indices. */
     for (i = 0; i < subtable->n_indices; i++) {
         hindex_remove(&subtable->indices[i], &rule->index_nodes[i]);
@@ -343,9 +460,30 @@ classifier_remove(struct classifier *cls, struct cls_rule *rule)
     } else {
         update_subtables_after_removal(cls, subtable, rule->priority);
     }
+
     cls->n_rules--;
 }
 
+/* Prefix tree context.  Valid when 'lookup_done' is true.  Can skip all
+ * subtables which have more than 'match_plen' bits in their corresponding
+ * field at offset 'be32ofs'.  If skipped, 'maskbits' prefix bits should be
+ * unwildcarded to quarantee datapath flow matches only packets it should. */
+struct trie_ctx {
+    const struct cls_trie *trie;
+    bool lookup_done;        /* Status of the lookup. */
+    uint8_t be32ofs;         /* U32 offset of the field in question. */
+    unsigned int match_plen; /* Longest prefix than could possibly match. */
+    unsigned int maskbits;   /* Prefix length needed to avoid false matches. */
+};
+
+static void
+trie_ctx_init(struct trie_ctx *ctx, const struct cls_trie *trie)
+{
+    ctx->trie = trie;
+    ctx->be32ofs = trie->field->flow_be32ofs;
+    ctx->lookup_done = false;
+}
+
 /* Finds and returns the highest-priority rule in 'cls' that matches 'flow'.
  * Returns a null pointer if no rules in 'cls' match 'flow'.  If multiple rules
  * of equal priority match 'flow', returns one arbitrarily.
@@ -362,6 +500,8 @@ classifier_lookup(const struct classifier *cls, const struct flow *flow,
     struct cls_subtable *subtable;
     struct cls_rule *best;
     tag_type tags;
+    struct trie_ctx trie_ctx[CLS_MAX_TRIES];
+    int i;
 
     /* Determine 'tags' such that, if 'subtable->tag' doesn't intersect them,
      * then 'flow' cannot possibly match in 'subtable':
@@ -387,6 +527,10 @@ classifier_lookup(const struct classifier *cls, const struct flow *flow,
                                   hash_metadata(flow->metadata)));
     tags = partition ? partition->tags : TAG_ARBITRARY;
 
+    /* Initialize trie contexts for match_find_wc(). */
+    for (i = 0; i < cls->n_tries; i++) {
+        trie_ctx_init(&trie_ctx[i], &cls->tries[i]);
+    }
     best = NULL;
     LIST_FOR_EACH (subtable, list_node, &cls->subtables_priority) {
         struct cls_rule *rule;
@@ -395,7 +539,7 @@ classifier_lookup(const struct classifier *cls, const struct flow *flow,
             continue;
         }
 
-        rule = find_match_wc(subtable, flow, wc);
+        rule = find_match_wc(subtable, flow, trie_ctx, cls->n_tries, wc);
         if (rule) {
             best = rule;
             LIST_FOR_EACH_CONTINUE (subtable, list_node,
@@ -409,7 +553,8 @@ classifier_lookup(const struct classifier *cls, const struct flow *flow,
                     continue;
                 }
 
-                rule = find_match_wc(subtable, flow, wc);
+                rule = find_match_wc(subtable, flow, trie_ctx, cls->n_tries,
+                                     wc);
                 if (rule && rule->priority > best->priority) {
                     best = rule;
                 }
@@ -417,6 +562,7 @@ classifier_lookup(const struct classifier *cls, const struct flow *flow,
             break;
         }
     }
+
     return best;
 }
 
@@ -708,6 +854,11 @@ insert_subtable(struct classifier *cls, const struct minimask *mask)
                      ? tag_create_deterministic(hash)
                      : TAG_ALL);
 
+    for (i = 0; i < cls->n_tries; i++) {
+        subtable->trie_plen[i] = minimask_get_prefix_len(mask,
+                                                         cls->tries[i].field);
+    }
+
     return subtable;
 }
 
@@ -820,6 +971,72 @@ update_subtables_after_removal(struct classifier *cls,
     }
 }
 
+struct range {
+    uint8_t start;
+    uint8_t end;
+};
+
+/* Return 'true' if can skip rest of the subtable based on the prefix trie
+ * lookup results. */
+static inline bool
+check_tries(struct trie_ctx trie_ctx[CLS_MAX_TRIES], unsigned int n_tries,
+            const unsigned int field_plen[CLS_MAX_TRIES],
+            const struct range ofs, const struct flow *flow,
+            struct flow_wildcards *wc)
+{
+    int j;
+
+    /* Check if we could avoid fully unwildcarding the next level of
+     * fields using the prefix tries.  The trie checks are done only as
+     * needed to avoid folding in additional bits to the wildcards mask. */
+    for (j = 0; j < n_tries; j++) {
+        /* Is the trie field relevant for this subtable? */
+        if (field_plen[j]) {
+            struct trie_ctx *ctx = &trie_ctx[j];
+            uint8_t be32ofs = ctx->be32ofs;
+
+            /* Is the trie field within the current range of fields? */
+            if (be32ofs >= ofs.start && be32ofs < ofs.end) {
+                /* On-demand trie lookup. */
+                if (!ctx->lookup_done) {
+                    ctx->match_plen = trie_lookup(ctx->trie, flow,
+                                                  &ctx->maskbits);
+                    ctx->lookup_done = true;
+                }
+                /* Possible to skip the rest of the subtable if subtable's
+                 * prefix on the field is longer than what is known to match
+                 * based on the trie lookup. */
+                if (field_plen[j] > ctx->match_plen) {
+                    /* RFC: We want the trie lookup to never result in
+                     * unwildcarding any bits that would not be unwildcarded
+                     * otherwise.  Since the trie is shared by the whole
+                     * classifier, it is possible that the 'maskbits' contain
+                     * bits that are irrelevant for the partition of the
+                     * classifier relevant for the current flow. */
+
+                    /* Can skip if the field is already unwildcarded. */
+                    if (mask_prefix_bits_set(wc, be32ofs, ctx->maskbits)) {
+                        return true;
+                    }
+                    /* Check that the trie result will not unwildcard more bits
+                     * than this stage will. */
+                    if (ctx->maskbits <= field_plen[j]) {
+                        /* Unwildcard the bits and skip the rest. */
+                        mask_set_prefix_bits(wc, be32ofs, ctx->maskbits);
+                        /* Note: Prerequisite already unwildcarded, as the only
+                         * prerequisite of the supported trie lookup fields is
+                         * the ethertype, which is currently always
+                         * unwildcarded.
+                         */
+                        return true;
+                    }
+                }
+            }
+        }
+    }
+    return false;
+}
+
 static inline struct cls_rule *
 find_match(const struct cls_subtable *subtable, const struct flow *flow,
            uint32_t hash)
@@ -837,32 +1054,37 @@ find_match(const struct cls_subtable *subtable, const struct flow *flow,
 
 static struct cls_rule *
 find_match_wc(const struct cls_subtable *subtable, const struct flow *flow,
-              struct flow_wildcards * wc)
+              struct trie_ctx trie_ctx[CLS_MAX_TRIES], unsigned int n_tries,
+              struct flow_wildcards *wc)
 {
     uint32_t basis = 0, hash;
     struct cls_rule *rule = NULL;
-    uint8_t prev_u32ofs = 0;
     int i;
+    struct range ofs;
 
     if (!wc) {
         return find_match(subtable, flow,
                           flow_hash_in_minimask(flow, &subtable->mask, 0));
     }
 
+    ofs.start = 0;
     /* Try to finish early by checking fields in segments. */
     for (i = 0; i < subtable->n_indices; i++) {
         struct hindex_node *inode;
+        ofs.end = subtable->index_ofs[i];
 
-        hash = flow_hash_in_minimask_range(flow, &subtable->mask, prev_u32ofs,
-                                           subtable->index_ofs[i], &basis);
-        prev_u32ofs = subtable->index_ofs[i];
+        if (check_tries(trie_ctx, n_tries, subtable->trie_plen, ofs, flow,
+                        wc)) {
+            goto range_out;
+        }
+        hash = flow_hash_in_minimask_range(flow, &subtable->mask, ofs.start,
+                                           ofs.end, &basis);
+        ofs.start = ofs.end;
         inode = hindex_node_with_hash(&subtable->indices[i], hash);
         if (!inode) {
             /* No match, can stop immediately, but must fold in the mask
              * covered so far. */
-            flow_wildcards_fold_minimask_range(wc, &subtable->mask, 0,
-                                               prev_u32ofs);
-            return NULL;
+            goto range_out;
         }
 
         /* If we have narrowed down to a single rule already, check whether
@@ -884,11 +1106,15 @@ find_match_wc(const struct cls_subtable *subtable, const struct flow *flow,
             }
         }
     }
-
+    ofs.end = FLOW_U32S;
+    /* Trie check for the final range. */
+    if (check_tries(trie_ctx, n_tries, subtable->trie_plen, ofs, flow, wc)) {
+        goto range_out;
+    }
     if (!rule) {
         /* Multiple potential matches exist, look for one. */
-        hash = flow_hash_in_minimask_range(flow, &subtable->mask, prev_u32ofs,
-                                           FLOW_U32S, &basis);
+        hash = flow_hash_in_minimask_range(flow, &subtable->mask, ofs.start,
+                                           ofs.end, &basis);
         rule = find_match(subtable, flow, hash);
     } else {
         /* We already narrowed the matching candidates down to just 'rule',
@@ -896,8 +1122,16 @@ find_match_wc(const struct cls_subtable *subtable, const struct flow *flow,
         rule = NULL;
     }
  out:
+    /* Must unwildcard all the fields, as they were looked at. */
     flow_wildcards_fold_minimask(wc, &subtable->mask);
     return rule;
+
+ range_out:
+    /* Must unwildcard the fields looked up so far, if any. */
+    if (ofs.start) {
+        flow_wildcards_fold_minimask_range(wc, &subtable->mask, 0, ofs.start);
+    }
+    return NULL;
 }
 
 static struct cls_rule *
@@ -922,16 +1156,16 @@ insert_rule(struct classifier *cls, struct cls_subtable *subtable,
     struct cls_rule *old = NULL;
     int i;
     uint32_t basis = 0, hash;
-    uint8_t prev_u32ofs = 0;
+    uint8_t prev_be32ofs = 0;
 
     /* Add new node to segment indices. */
     for (i = 0; i < subtable->n_indices; i++) {
-        hash = minimatch_hash_range(&new->match, prev_u32ofs,
+        hash = minimatch_hash_range(&new->match, prev_be32ofs,
                                     subtable->index_ofs[i], &basis);
         hindex_insert(&subtable->indices[i], &new->index_nodes[i], hash);
-        prev_u32ofs = subtable->index_ofs[i];
+        prev_be32ofs = subtable->index_ofs[i];
     }
-    hash = minimatch_hash_range(&new->match, prev_u32ofs, FLOW_U32S, &basis);
+    hash = minimatch_hash_range(&new->match, prev_be32ofs, FLOW_U32S, &basis);
     head = find_equal(subtable, &new->match.flow, hash);
     if (!head) {
         hmap_insert(&subtable->rules, &new->hmap_node, hash);
@@ -992,3 +1226,393 @@ next_rule_in_list(struct cls_rule *rule)
     struct cls_rule *next = next_rule_in_list__(rule);
     return next->priority < rule->priority ? next : NULL;
 }
+\f
+/* A longest-prefix match tree. */
+struct trie_node {
+    uint32_t prefix;           /* Prefix bits for this node, MSB first. */
+    uint8_t  nbits;            /* Never zero, except for the root node. */
+    unsigned int n_rules;      /* Number of rules that have this prefix. */
+    struct trie_node *edges[2]; /* Both NULL if leaf. */
+};
+
+/* Max bits per node.  Must fit in struct trie_node's 'prefix'.
+ * Also tested with 16, 8, and 5 to stress the implementation. */
+#define TRIE_PREFIX_BITS 32
+
+/* Return at least 'plen' bits of the 'prefix', starting at bit offset 'ofs'.
+ * Prefixes are in the network byte order, and the offset 0 corresponds to
+ * the most significant bit of the first byte.  The offset can be read as
+ * "how many bits to skip from the start of the prefix starting at 'pr'". */
+static uint32_t
+raw_get_prefix(const ovs_be32 pr[], unsigned int ofs, unsigned int plen)
+{
+    uint32_t prefix;
+
+    pr += ofs / 32; /* Where to start. */
+    ofs %= 32;      /* How many bits to skip at 'pr'. */
+
+    prefix = ntohl(*pr) << ofs; /* Get the first 32 - ofs bits. */
+    if (plen > 32 - ofs) {      /* Need more than we have already? */
+        prefix |= ntohl(*++pr) >> (32 - ofs);
+    }
+    /* Return with possible unwanted bits at the end. */
+    return prefix;
+}
+
+/* Return min(TRIE_PREFIX_BITS, plen) bits of the 'prefix', starting at bit
+ * offset 'ofs'.  Prefixes are in the network byte order, and the offset 0
+ * corresponds to the most significant bit of the first byte.  The offset can
+ * be read as "how many bits to skip from the start of the prefix starting at
+ * 'pr'". */
+static uint32_t
+trie_get_prefix(const ovs_be32 pr[], unsigned int ofs, unsigned int plen)
+{
+    if (!plen) {
+        return 0;
+    }
+    if (plen > TRIE_PREFIX_BITS) {
+        plen = TRIE_PREFIX_BITS; /* Get at most TRIE_PREFIX_BITS. */
+    }
+    /* Return with unwanted bits cleared. */
+    return raw_get_prefix(pr, ofs, plen) & ~0u << (32 - plen);
+}
+
+/* Return the number of equal bits in 'nbits' of 'prefix's MSBs and a 'value'
+ * starting at "MSB 0"-based offset 'ofs'. */
+static unsigned int
+prefix_equal_bits(uint32_t prefix, unsigned int nbits, const ovs_be32 value[],
+                  unsigned int ofs)
+{
+    uint64_t diff = prefix ^ raw_get_prefix(value, ofs, nbits);
+    /* Set the bit after the relevant bits to limit the result. */
+    return raw_clz64(diff << 32 | UINT64_C(1) << (63 - nbits));
+}
+
+/* Return the number of equal bits in 'node' prefix and a 'prefix' of length
+ * 'plen', starting at "MSB 0"-based offset 'ofs'. */
+static unsigned int
+trie_prefix_equal_bits(const struct trie_node *node, const ovs_be32 prefix[],
+                       unsigned int ofs, unsigned int plen)
+{
+    return prefix_equal_bits(node->prefix, MIN(node->nbits, plen - ofs),
+                             prefix, ofs);
+}
+
+/* Return the bit at ("MSB 0"-based) offset 'ofs' as an int.  'ofs' can
+ * be greater than 31. */
+static unsigned int
+be_get_bit_at(const ovs_be32 value[], unsigned int ofs)
+{
+    return (((const uint8_t *)value)[ofs / 8] >> (7 - ofs % 8)) & 1u;
+}
+
+/* Return the bit at ("MSB 0"-based) offset 'ofs' as an int.  'ofs' must
+ * be between 0 and 31, inclusive. */
+static unsigned int
+get_bit_at(const uint32_t prefix, unsigned int ofs)
+{
+    return (prefix >> (31 - ofs)) & 1u;
+}
+
+/* Create new branch. */
+static struct trie_node *
+trie_branch_create(const ovs_be32 *prefix, unsigned int ofs, unsigned int plen,
+                   unsigned int n_rules)
+{
+    struct trie_node *node = xmalloc(sizeof *node);
+
+    node->prefix = trie_get_prefix(prefix, ofs, plen);
+
+    if (plen <= TRIE_PREFIX_BITS) {
+        node->nbits = plen;
+        node->edges[0] = NULL;
+        node->edges[1] = NULL;
+        node->n_rules = n_rules;
+    } else { /* Need intermediate nodes. */
+        struct trie_node *subnode = trie_branch_create(prefix,
+                                                       ofs + TRIE_PREFIX_BITS,
+                                                       plen - TRIE_PREFIX_BITS,
+                                                       n_rules);
+        int bit = get_bit_at(subnode->prefix, 0);
+        node->nbits = TRIE_PREFIX_BITS;
+        node->edges[bit] = subnode;
+        node->edges[!bit] = NULL;
+        node->n_rules = 0;
+    }
+    return node;
+}
+
+static void
+trie_node_destroy(struct trie_node *node)
+{
+    free(node);
+}
+
+static void
+trie_destroy(struct trie_node *node)
+{
+    if (node) {
+        trie_destroy(node->edges[0]);
+        trie_destroy(node->edges[1]);
+        free(node);
+    }
+}
+
+static bool
+trie_is_leaf(const struct trie_node *trie)
+{
+    return !trie->edges[0] && !trie->edges[1]; /* No children. */
+}
+
+static void
+mask_set_prefix_bits(struct flow_wildcards *wc, uint8_t be32ofs,
+                     unsigned int nbits)
+{
+    ovs_be32 *mask = &((ovs_be32 *)&wc->masks)[be32ofs];
+    unsigned int i;
+
+    for (i = 0; i < nbits / 32; i++) {
+        mask[i] = OVS_BE32_MAX;
+    }
+    if (nbits % 32) {
+        mask[i] |= htonl(~0u << (32 - nbits % 32));
+    }
+}
+
+static bool
+mask_prefix_bits_set(const struct flow_wildcards *wc, uint8_t be32ofs,
+                     unsigned int nbits)
+{
+    ovs_be32 *mask = &((ovs_be32 *)&wc->masks)[be32ofs];
+    unsigned int i;
+    ovs_be32 zeroes = 0;
+
+    for (i = 0; i < nbits / 32; i++) {
+        zeroes |= ~mask[i];
+    }
+    if (nbits % 32) {
+        zeroes |= ~mask[i] & htonl(~0u << (32 - nbits % 32));
+    }
+
+    return !zeroes; /* All 'nbits' bits set. */
+}
+
+static struct trie_node **
+trie_next_edge(struct trie_node *node, const ovs_be32 value[],
+               unsigned int ofs)
+{
+    return node->edges + be_get_bit_at(value, ofs);
+}
+
+static const struct trie_node *
+trie_next_node(const struct trie_node *node, const ovs_be32 value[],
+               unsigned int ofs)
+{
+    return node->edges[be_get_bit_at(value, ofs)];
+}
+
+/* Return the prefix mask length necessary to find the longest-prefix match for
+ * the '*value' in the prefix tree 'node'.
+ * '*checkbits' is set to the number of bits in the prefix mask necessary to
+ * determine a mismatch, in case there are longer prefixes in the tree below
+ * the one that matched.
+ */
+static unsigned int
+trie_lookup_value(const struct trie_node *node, const ovs_be32 value[],
+                  unsigned int *checkbits)
+{
+    unsigned int plen = 0, match_len = 0;
+    const struct trie_node *prev = NULL;
+
+    for (; node; prev = node, node = trie_next_node(node, value, plen)) {
+        unsigned int eqbits;
+        /* Check if this edge can be followed. */
+        eqbits = prefix_equal_bits(node->prefix, node->nbits, value, plen);
+        plen += eqbits;
+        if (eqbits < node->nbits) { /* Mismatch, nothing more to be found. */
+            /* Bit at offset 'plen' differed. */
+            *checkbits = plen + 1; /* Includes the first mismatching bit. */
+            return match_len;
+        }
+        /* Full match, check if rules exist at this prefix length. */
+        if (node->n_rules > 0) {
+            match_len = plen;
+        }
+    }
+    /* Dead end, exclude the other branch if it exists. */
+    *checkbits = !prev || trie_is_leaf(prev) ? plen : plen + 1;
+    return match_len;
+}
+
+static unsigned int
+trie_lookup(const struct cls_trie *trie, const struct flow *flow,
+            unsigned int *checkbits)
+{
+    const struct mf_field *mf = trie->field;
+
+    /* Check that current flow matches the prerequisites for the trie
+     * field.  Some match fields are used for multiple purposes, so we
+     * must check that the trie is relevant for this flow. */
+    if (mf_are_prereqs_ok(mf, flow)) {
+        return trie_lookup_value(trie->root,
+                                 &((ovs_be32 *)flow)[mf->flow_be32ofs],
+                                 checkbits);
+    }
+    *checkbits = 0; /* Value not used in this case. */
+    return UINT_MAX;
+}
+
+/* Returns the length of a prefix match mask for the field 'mf' in 'minimask'.
+ * Returns the u32 offset to the miniflow data in '*miniflow_index', if
+ * 'miniflow_index' is not NULL. */
+static unsigned int
+minimask_get_prefix_len(const struct minimask *minimask,
+                        const struct mf_field *mf)
+{
+    unsigned int nbits = 0, mask_tz = 0; /* Non-zero when end of mask seen. */
+    uint8_t u32_ofs = mf->flow_be32ofs;
+    uint8_t u32_end = u32_ofs + mf->n_bytes / 4;
+
+    for (; u32_ofs < u32_end; ++u32_ofs) {
+        uint32_t mask;
+        mask = ntohl((OVS_FORCE ovs_be32)minimask_get(minimask, u32_ofs));
+
+        /* Validate mask, count the mask length. */
+        if (mask_tz) {
+            if (mask) {
+                return 0; /* No bits allowed after mask ended. */
+            }
+        } else {
+            if (~mask & (~mask + 1)) {
+                return 0; /* Mask not contiguous. */
+            }
+            mask_tz = ctz32(mask);
+            nbits += 32 - mask_tz;
+        }
+    }
+
+    return nbits;
+}
+
+/*
+ * This is called only when mask prefix is known to be CIDR and non-zero.
+ * Relies on the fact that the flow and mask have the same map, and since
+ * the mask is CIDR, the storage for the flow field exists even if it
+ * happened to be zeros.
+ */
+static const ovs_be32 *
+minimatch_get_prefix(const struct minimatch *match, const struct mf_field *mf)
+{
+    return (OVS_FORCE const ovs_be32 *)match->flow.values +
+        count_1bits(match->flow.map & ((UINT64_C(1) << mf->flow_be32ofs) - 1));
+}
+
+/* Insert rule in to the prefix tree.
+ * 'mlen' must be the (non-zero) CIDR prefix length of the 'trie->field' mask
+ * in 'rule'. */
+static void
+trie_insert(struct cls_trie *trie, const struct cls_rule *rule, int mlen)
+{
+    const ovs_be32 *prefix = minimatch_get_prefix(&rule->match, trie->field);
+    struct trie_node *node;
+    struct trie_node **edge;
+    int ofs = 0;
+
+    /* Walk the tree. */
+    for (edge = &trie->root;
+         (node = *edge) != NULL;
+         edge = trie_next_edge(node, prefix, ofs)) {
+        unsigned int eqbits = trie_prefix_equal_bits(node, prefix, ofs, mlen);
+        ofs += eqbits;
+        if (eqbits < node->nbits) {
+            /* Mismatch, new node needs to be inserted above. */
+            int old_branch = get_bit_at(node->prefix, eqbits);
+
+            /* New parent node. */
+            *edge = trie_branch_create(prefix, ofs - eqbits, eqbits,
+                                       ofs == mlen ? 1 : 0);
+
+            /* Adjust old node for its new position in the tree. */
+            node->prefix <<= eqbits;
+            node->nbits -= eqbits;
+            (*edge)->edges[old_branch] = node;
+
+            /* Check if need a new branch for the new rule. */
+            if (ofs < mlen) {
+                (*edge)->edges[!old_branch]
+                    = trie_branch_create(prefix, ofs, mlen - ofs, 1);
+            }
+            return;
+        }
+        /* Full match so far. */
+
+        if (ofs == mlen) {
+            /* Full match at the current node, rule needs to be added here. */
+            node->n_rules++;
+            return;
+        }
+    }
+    /* Must insert a new tree branch for the new rule. */
+    *edge = trie_branch_create(prefix, ofs, mlen - ofs, 1);
+}
+
+/* 'mlen' must be the (non-zero) CIDR prefix length of the 'trie->field' mask
+ * in 'rule'. */
+static void
+trie_remove(struct cls_trie *trie, const struct cls_rule *rule, int mlen)
+{
+    const ovs_be32 *prefix = minimatch_get_prefix(&rule->match, trie->field);
+    struct trie_node *node;
+    struct trie_node **edges[sizeof(union mf_value) * 8];
+    int depth = 0, ofs = 0;
+
+    /* Walk the tree. */
+    for (edges[depth] = &trie->root;
+         (node = *edges[depth]) != NULL;
+         edges[++depth] = trie_next_edge(node, prefix, ofs)) {
+        unsigned int eqbits = trie_prefix_equal_bits(node, prefix, ofs, mlen);
+        if (eqbits < node->nbits) {
+            /* Mismatch, nothing to be removed.  This should never happen, as
+             * only rules in the classifier are ever removed. */
+            break; /* Log a warning. */
+        }
+        /* Full match so far. */
+        ofs += eqbits;
+
+        if (ofs == mlen) {
+            /* Full prefix match at the current node, remove rule here. */
+            if (!node->n_rules) {
+                break; /* Log a warning. */
+            }
+            node->n_rules--;
+
+            /* Check if can prune the tree. */
+            while (!node->n_rules && !(node->edges[0] && node->edges[1])) {
+                /* No rules and at most one child node, remove this node. */
+                struct trie_node *next;
+                next = node->edges[0] ? node->edges[0] : node->edges[1];
+
+                if (next) {
+                    if (node->nbits + next->nbits > TRIE_PREFIX_BITS) {
+                        break;   /* Cannot combine. */
+                    }
+                    /* Combine node with next. */
+                    next->prefix = node->prefix | next->prefix >> node->nbits;
+                    next->nbits += node->nbits;
+                }
+                trie_node_destroy(node);
+                /* Update the parent's edge. */
+                *edges[depth] = next;
+                if (next || !depth) {
+                    /* Branch not pruned or at root, nothing more to do. */
+                    break;
+                }
+                node = *edges[--depth];
+            }
+            return;
+        }
+    }
+    /* Cannot go deeper. This should never happen, since only rules
+     * that actually exist in the classifier are ever removed. */
+    VLOG_WARN("Trying to remove non-existing rule from a prefix trie.");
+}
index 3c3e7d1..b6b89a0 100644 (file)
  * =====
  *
  * A flow classifier holds any number of "rules", each of which specifies
- * values to match for some fields or subfields and a priority.  The primary
- * design goal for the classifier is that, given a packet, it can as quickly as
- * possible find the highest-priority rule that matches the packet.
+ * values to match for some fields or subfields and a priority.  Each OpenFlow
+ * table is implemented as a flow classifier.
  *
- * Each OpenFlow table is implemented as a flow classifier.
+ * The classifier has two primary design goals.  The first is obvious: given a
+ * set of packet headers, as quickly as possible find the highest-priority rule
+ * that matches those headers.  The following section describes the second
+ * goal.
  *
  *
- * Basic Design
- * ============
+ * "Un-wildcarding"
+ * ================
+ *
+ * A primary goal of the flow classifier is to produce, as a side effect of a
+ * packet lookup, a wildcard mask that indicates which bits of the packet
+ * headers were essential to the classification result.  Ideally, a 1-bit in
+ * any position of this mask means that, if the corresponding bit in the packet
+ * header were flipped, then the classification result might change.  A 0-bit
+ * means that changing the packet header bit would have no effect.  Thus, the
+ * wildcarded bits are the ones that played no role in the classification
+ * decision.
+ *
+ * Such a wildcard mask is useful with datapaths that support installing flows
+ * that wildcard fields or subfields.  If an OpenFlow lookup for a TCP flow
+ * does not actually look at the TCP source or destination ports, for example,
+ * then the switch may install into the datapath a flow that wildcards the port
+ * numbers, which in turn allows the datapath to handle packets that arrive for
+ * other TCP source or destination ports without additional help from
+ * ovs-vswitchd.  This is useful for the Open vSwitch software and,
+ * potentially, for ASIC-based switches as well.
+ *
+ * Some properties of the wildcard mask:
+ *
+ *     - "False 1-bits" are acceptable, that is, setting a bit in the wildcard
+ *       mask to 1 will never cause a packet to be forwarded the wrong way.
+ *       As a corollary, a wildcard mask composed of all 1-bits will always
+ *       yield correct (but often needlessly inefficient) behavior.
+ *
+ *     - "False 0-bits" can cause problems, so they must be avoided.  In the
+ *       extreme case, a mask of all 0-bits is only correct if the classifier
+ *       contains only a single flow that matches all packets.
+ *
+ *     - 0-bits are desirable because they allow the datapath to act more
+ *       autonomously, relying less on ovs-vswitchd to process flow setups,
+ *       thereby improving performance.
+ *
+ *     - We don't know a good way to generate wildcard masks with the maximum
+ *       (correct) number of 0-bits.  We use various approximations, described
+ *       in later sections.
+ *
+ *     - Wildcard masks for lookups in a given classifier yield a
+ *       non-overlapping set of rules.  More specifically:
+ *
+ *       Consider an classifier C1 filled with an arbitrary collection of rules
+ *       and an empty classifier C2.  Now take a set of packet headers H and
+ *       look it up in C1, yielding a highest-priority matching rule R1 and
+ *       wildcard mask M.  Form a new classifier rule R2 out of packet headers
+ *       H and mask M, and add R2 to C2 with a fixed priority.  If one were to
+ *       do this for every possible set of packet headers H, then this
+ *       process would not attempt to add any overlapping rules to C2, that is,
+ *       any packet lookup using the rules generated by this process matches at
+ *       most one rule in C2.
+ *
+ * During the lookup process, the classifier starts out with a wildcard mask
+ * that is all 0-bits, that is, fully wildcarded.  As lookup proceeds, each
+ * step tends to add constraints to the wildcard mask, that is, change
+ * wildcarded 0-bits into exact-match 1-bits.  We call this "un-wildcarding".
+ * A lookup step that examines a particular field must un-wildcard that field.
+ * In general, un-wildcarding is necessary for correctness but undesirable for
+ * performance.
+ *
+ *
+ * Basic Classifier Design
+ * =======================
  *
  * Suppose that all the rules in a classifier had the same form.  For example,
  * suppose that they all matched on the source and destination Ethernet address
  * cls_subtable" in the classifier and tracks the highest-priority match that
  * it finds.  The subtables are kept in a descending priority order according
  * to the highest priority rule in each subtable, which allows lookup to skip
- * over subtables that can't possibly have a higher-priority match than
- * already found.
+ * over subtables that can't possibly have a higher-priority match than already
+ * found.  Eliminating lookups through priority ordering aids both classifier
+ * primary design goals: skipping lookups saves time and avoids un-wildcarding
+ * fields that those lookups would have examined.
  *
  * One detail: a classifier can contain multiple rules that are identical other
  * than their priority.  When this happens, only the highest priority rule out
  * list inside that highest-priority rule.
  *
  *
- * Staged Lookup
- * =============
+ * Staged Lookup (Wildcard Optimization)
+ * =====================================
  *
  * Subtable lookup is performed in ranges defined for struct flow, starting
  * from metadata (registers, in_port, etc.), then L2 header, L3, and finally
  * L4 ports.  Whenever it is found that there are no matches in the current
- * subtable, the rest of the subtable can be skipped.  The rationale of this
- * logic is that as many fields as possible can remain wildcarded.
+ * subtable, the rest of the subtable can be skipped.
+ *
+ * Staged lookup does not reduce lookup time, and it may increase it, because
+ * it changes a single hash table lookup into multiple hash table lookups.
+ * It reduces un-wildcarding significantly in important use cases.
  *
  *
- * Partitioning
- * ============
+ * Prefix Tracking (Wildcard Optimization)
+ * =======================================
+ *
+ * Classifier uses prefix trees ("tries") for tracking the used
+ * address space, enabling skipping classifier tables containing
+ * longer masks than necessary for the given address.  This reduces
+ * un-wildcarding for datapath flows in parts of the address space
+ * without host routes, but consulting extra data structures (the
+ * tries) may slightly increase lookup time.
+ *
+ * Trie lookup is interwoven with staged lookup, so that a trie is
+ * searched only when the configured trie field becomes relevant for
+ * the lookup.  The trie lookup results are retained so that each trie
+ * is checked at most once for each classifier lookup.
+ *
+ * This implementation tracks the number of rules at each address
+ * prefix for the whole classifier.  More aggressive table skipping
+ * would be possible by maintaining lists of tables that have prefixes
+ * at the lengths encountered on tree traversal, or by maintaining
+ * separate tries for subsets of rules separated by metadata fields.
+ *
+ * Prefix tracking is configured via OVSDB "Flow_Table" table,
+ * "fieldspec" column.  "fieldspec" is a string map where a "prefix"
+ * key tells which fields should be used for prefix tracking.  The
+ * value of the "prefix" key is a comma separated list of field names.
+ *
+ * There is a maximum number of fields that can be enabled for any one
+ * flow table.  Currently this limit is 3.
+ *
+ *
+ * Partitioning (Lookup Time and Wildcard Optimization)
+ * ====================================================
  *
  * Suppose that a given classifier is being used to handle multiple stages in a
  * pipeline using "resubmit", with metadata (that is, the OpenFlow 1.1+ field
  * any cls_subtable that doesn't match on metadata.  We handle that by giving
  * any such cls_subtable TAG_ALL as its 'tags' so that it matches any tag.)
  *
+ * Partitioning saves lookup time by reducing the number of subtable lookups.
+ * Each eliminated subtable lookup also reduces the amount of un-wildcarding.
+ *
  *
  * Thread-safety
  * =============
 #include "hmap.h"
 #include "list.h"
 #include "match.h"
+#include "meta-flow.h"
 #include "tag.h"
 #include "openflow/nicira-ext.h"
 #include "openflow/openflow.h"
@@ -128,9 +231,18 @@ extern "C" {
 
 /* Needed only for the lock annotation in struct classifier. */
 extern struct ovs_mutex ofproto_mutex;
+struct trie_node;
+
+/* Prefix trie for a 'field' */
+struct cls_trie {
+    const struct mf_field *field; /* Trie field, or NULL. */
+    struct trie_node *root;       /* NULL if none. */
+};
 
-/* Maximum number of staged lookup indices for each subtable. */
-enum { CLS_MAX_INDICES = 3 };
+enum {
+    CLS_MAX_INDICES = 3, /* Maximum number of lookup indices per subtable. */
+    CLS_MAX_TRIES = 3    /* Maximum number of prefix trees per classifier. */
+};
 
 /* A flow classifier. */
 struct classifier {
@@ -143,6 +255,8 @@ struct classifier {
                                      */
     struct hmap partitions;     /* Contains "struct cls_partition"s. */
     struct ovs_rwlock rwlock OVS_ACQ_AFTER(ofproto_mutex);
+    struct cls_trie tries[CLS_MAX_TRIES]; /* Prefix tries. */
+    unsigned int n_tries;
 };
 
 /* A set of rules that all have the same fields wildcarded. */
@@ -160,6 +274,7 @@ struct cls_subtable {
     uint8_t n_indices;           /* How many indices to use. */
     uint8_t index_ofs[CLS_MAX_INDICES]; /* u32 flow segment boundaries. */
     struct hindex indices[CLS_MAX_INDICES]; /* Staged lookup indices. */
+    unsigned int trie_plen[CLS_MAX_TRIES];  /* Trie prefix length in 'mask'. */
 };
 
 /* Returns true if 'table' is a "catch-all" subtable that will match every
@@ -211,6 +326,11 @@ bool cls_rule_is_loose_match(const struct cls_rule *rule,
 
 void classifier_init(struct classifier *cls, const uint8_t *flow_segments);
 void classifier_destroy(struct classifier *);
+void classifier_set_prefix_fields(struct classifier *cls,
+                                  const enum mf_field_id *trie_fields,
+                                  unsigned int n_trie_fields)
+    OVS_REQ_WRLOCK(cls->rwlock);
+
 bool classifier_is_empty(const struct classifier *cls)
     OVS_REQ_RDLOCK(cls->rwlock);
 int classifier_count(const struct classifier *cls)
index 0dbacbf..e867d72 100644 (file)
@@ -96,8 +96,8 @@
  *    - OVS_REQUIRES(MUTEX) indicate that the function may only be called with
  *      MUTEX held and that the function does not release MUTEX.
  *
- *    - OVS_LOCKS_EXCLUDED(MUTEX) indicates that the function may only be
- *      called when MUTEX is not held.
+ *    - OVS_EXCLUDED(MUTEX) indicates that the function may only be called when
+ *      MUTEX is not held.
  *
  *
  * The following variants, with the same syntax, apply to reader-writer locks:
  *    OVS_RELEASES         OVS_RELEASES         OVS_RELEASES
  *    OVS_TRY_LOCK         OVS_TRY_RDLOCK       OVS_TRY_WRLOCK
  *    OVS_REQUIRES         OVS_REQ_RDLOCK       OVS_REQ_WRLOCK
- *    OVS_LOCKS_EXCLUDED   OVS_LOCKS_EXCLUDED   OVS_LOCKS_EXCLUDED
+ *    OVS_EXCLUDED         OVS_EXCLUDED         OVS_EXCLUDED
  */
 #define OVS_LOCKABLE __attribute__((lockable))
 #define OVS_REQ_RDLOCK(...) __attribute__((shared_locks_required(__VA_ARGS__)))
 #define OVS_PACKED(DECL) __pragma(pack(push, 1)) DECL __pragma(pack(pop))
 #endif
 
+#ifdef _MSC_VER
+#define CCALL __cdecl
+#pragma section(".CRT$XCU",read)
+#define OVS_CONSTRUCTOR(f) \
+    static void __cdecl f(void); \
+    __declspec(allocate(".CRT$XCU")) void (__cdecl*f##_)(void) = f; \
+    static void __cdecl f(void)
+#else
+#define OVS_CONSTRUCTOR(f) \
+    static void f(void) __attribute__((constructor)); \
+    static void f(void)
+#endif
+
 #endif /* compiler.h */
diff --git a/lib/connectivity.c b/lib/connectivity.c
new file mode 100644 (file)
index 0000000..c80b5f1
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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 "connectivity.h"
+#include "ovs-thread.h"
+#include "seq.h"
+
+static struct seq *connectivity_seq;
+
+/* Provides a global seq for connectivity changes.
+ *
+ * Connectivity monitoring modules should call seq_change() on the returned
+ * object whenever the status of a port changes, whether the cause is local or
+ * remote.
+ *
+ * Clients can seq_wait() on this object for changes to netdev flags, features,
+ * ethernet addresses, carrier changes, and bfd/cfm/lacp/stp status. */
+struct seq *
+connectivity_seq_get(void)
+{
+    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+
+    if (ovsthread_once_start(&once)) {
+        connectivity_seq = seq_create();
+        ovsthread_once_done(&once);
+    }
+
+    return connectivity_seq;
+}
diff --git a/lib/connectivity.h b/lib/connectivity.h
new file mode 100644 (file)
index 0000000..123e886
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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 CONNECTIVITY_H
+#define CONNECTIVITY_H 1
+
+#include <stdint.h>
+
+/* For tracking connectivity changes globally. */
+struct seq *connectivity_seq_get(void);
+
+#endif /* connectivity.h */
index aae9937..cc17e34 100644 (file)
 VLOG_DEFINE_THIS_MODULE(coverage);
 
 /* The coverage counters. */
-#if USE_LINKER_SECTIONS
-extern struct coverage_counter *__start_coverage[];
-extern struct coverage_counter *__stop_coverage[];
-#define coverage_counters __start_coverage
-#define n_coverage_counters  (__stop_coverage - __start_coverage)
-#else  /* !USE_LINKER_SECTIONS */
-#define COVERAGE_COUNTER(COUNTER)                                       \
-        DECLARE_EXTERN_PER_THREAD_DATA(unsigned int,                    \
-                                       counter_##COUNTER);              \
-        DEFINE_EXTERN_PER_THREAD_DATA(counter_##COUNTER, 0);            \
-        static unsigned int COUNTER##_count(void)                       \
-        {                                                               \
-            unsigned int *countp = counter_##COUNTER##_get();           \
-            unsigned int count = *countp;                               \
-            *countp = 0;                                                \
-            return count;                                               \
-        }                                                               \
-        extern struct coverage_counter counter_##COUNTER;               \
-        struct coverage_counter counter_##COUNTER                       \
-            = { #COUNTER, COUNTER##_count, 0 };
-#include "coverage.def"
-#undef COVERAGE_COUNTER
-
-extern struct coverage_counter *coverage_counters[];
-struct coverage_counter *coverage_counters[] = {
-#define COVERAGE_COUNTER(NAME) &counter_##NAME,
-#include "coverage.def"
-#undef COVERAGE_COUNTER
-};
-#define n_coverage_counters ARRAY_SIZE(coverage_counters)
-#endif  /* !USE_LINKER_SECTIONS */
+static struct coverage_counter **coverage_counters = NULL;
+static size_t n_coverage_counters = 0;
+static size_t allocated_coverage_counters = 0;
 
 static struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
 
@@ -73,6 +45,18 @@ static void coverage_read(struct svec *);
 static unsigned int coverage_array_sum(const unsigned int *arr,
                                        const unsigned int len);
 
+/* Registers a coverage counter with the coverage core */
+void
+coverage_counter_register(struct coverage_counter* counter)
+{
+    if (n_coverage_counters >= allocated_coverage_counters) {
+        coverage_counters = x2nrealloc(coverage_counters,
+                                       &allocated_coverage_counters,
+                                       sizeof(struct coverage_counter*));
+    }
+    coverage_counters[n_coverage_counters++] = counter;
+}
+
 static void
 coverage_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
                      const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
index 4e6c050..0b41b00 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "ovs-thread.h"
 #include "vlog.h"
+#include "compiler.h"
 
 /* Makes coverage_run run every 5000 ms (5 seconds).
  * If this value is redefined, the new value must
@@ -54,9 +55,10 @@ struct coverage_counter {
     unsigned int hr[HR_AVG_LEN];
 };
 
+void coverage_counter_register(struct coverage_counter*);
+
 /* Defines COUNTER.  There must be exactly one such definition at file scope
  * within a program. */
-#if USE_LINKER_SECTIONS
 #define COVERAGE_DEFINE(COUNTER)                                        \
         DEFINE_STATIC_PER_THREAD_DATA(unsigned int,                     \
                                       counter_##COUNTER, 0);            \
@@ -74,19 +76,9 @@ struct coverage_counter {
         extern struct coverage_counter counter_##COUNTER;               \
         struct coverage_counter counter_##COUNTER                       \
             = { #COUNTER, COUNTER##_count, 0, 0, {0}, {0} };            \
-        extern struct coverage_counter *counter_ptr_##COUNTER;          \
-        struct coverage_counter *counter_ptr_##COUNTER                  \
-            __attribute__((section("coverage"))) = &counter_##COUNTER
-#else
-#define COVERAGE_DEFINE(COUNTER)                                        \
-        DECLARE_EXTERN_PER_THREAD_DATA(unsigned int,                    \
-                                       counter_##COUNTER);              \
-        static inline void COUNTER##_add(unsigned int n)                \
-        {                                                               \
-            *counter_##COUNTER##_get() += n;                            \
-        }                                                               \
-        extern struct coverage_counter counter_##COUNTER
-#endif
+        OVS_CONSTRUCTOR(COUNTER##_init) {                               \
+            coverage_counter_register(&counter_##COUNTER);              \
+        }
 
 /* Adds 1 to COUNTER. */
 #define COVERAGE_INC(COUNTER) COVERAGE_ADD(COUNTER, 1)
index 54641d0..5ed2895 100644 (file)
@@ -341,7 +341,7 @@ fork_and_wait_for_startup(int *fdp)
             } else if (retval < 0) {
                 VLOG_FATAL("waitpid failed (%s)", ovs_strerror(errno));
             } else {
-                NOT_REACHED();
+                OVS_NOT_REACHED();
             }
         }
         close(fds[0]);
index 25715f4..3587bbd 100644 (file)
@@ -73,6 +73,7 @@ struct dpif_linux_dp {
     /* Attributes. */
     const char *name;                  /* OVS_DP_ATTR_NAME. */
     const uint32_t *upcall_pid;        /* OVS_DP_ATTR_UPCALL_PID. */
+    uint32_t user_features;            /* OVS_DP_ATTR_USER_FEATURES */
     struct ovs_dp_stats stats;         /* OVS_DP_ATTR_STATS. */
     struct ovs_dp_megaflow_stats megaflow_stats;
                                        /* OVS_DP_ATTR_MEGAFLOW_STATS.*/
@@ -151,6 +152,7 @@ struct dpif_linux {
 
     /* Change notification. */
     struct nl_sock *port_notifier; /* vport multicast group subscriber. */
+    bool refresh_channels;
 };
 
 static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5);
@@ -172,6 +174,7 @@ static int dpif_linux_init(void);
 static int open_dpif(const struct dpif_linux_dp *, struct dpif **);
 static uint32_t dpif_linux_port_get_pid(const struct dpif *,
                                         odp_port_t port_no);
+static int dpif_linux_refresh_channels(struct dpif *);
 
 static void dpif_linux_vport_to_ofpbuf(const struct dpif_linux_vport *,
                                        struct ofpbuf *);
@@ -229,9 +232,11 @@ dpif_linux_open(const struct dpif_class *class OVS_UNUSED, const char *name,
         upcall_pid = 0;
         dp_request.upcall_pid = &upcall_pid;
     } else {
-        dp_request.cmd = OVS_DP_CMD_GET;
+        /* Use OVS_DP_CMD_SET to report user features */
+        dp_request.cmd = OVS_DP_CMD_SET;
     }
     dp_request.name = name;
+    dp_request.user_features |= OVS_DP_F_UNALIGNED;
     error = dpif_linux_dp_transact(&dp_request, &dp, &buf);
     if (error) {
         return error;
@@ -400,6 +405,16 @@ dpif_linux_destroy(struct dpif *dpif_)
     return dpif_linux_dp_transact(&dp, NULL, NULL);
 }
 
+static void
+dpif_linux_run(struct dpif *dpif_)
+{
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+    if (dpif->refresh_channels) {
+        dpif->refresh_channels = false;
+        dpif_linux_refresh_channels(dpif_);
+    }
+}
+
 static int
 dpif_linux_get_stats(const struct dpif *dpif_, struct dpif_dp_stats *stats)
 {
@@ -676,7 +691,8 @@ dpif_linux_port_get_pid(const struct dpif *dpif_, odp_port_t port_no)
         /* The ODPP_NONE "reserved" port number uses the "ovs-system"'s
          * channel, since it is not heavily loaded. */
         uint32_t idx = port_idx >= dpif->uc_array_size ? 0 : port_idx;
-        pid = nl_sock_pid(dpif->channels[idx].sock);
+        const struct nl_sock *sock = dpif->channels[idx].sock;
+        pid = sock ? nl_sock_pid(sock) : 0;
     }
     ovs_mutex_unlock(&dpif->upcall_lock);
 
@@ -699,46 +715,66 @@ struct dpif_linux_port_state {
     struct nl_dump dump;
 };
 
-static int
-dpif_linux_port_dump_start(const struct dpif *dpif_, void **statep)
+static void
+dpif_linux_port_dump_start__(const struct dpif *dpif_, struct nl_dump *dump)
 {
     const struct dpif_linux *dpif = dpif_linux_cast(dpif_);
-    struct dpif_linux_port_state *state;
     struct dpif_linux_vport request;
     struct ofpbuf *buf;
 
-    *statep = state = xmalloc(sizeof *state);
-
     dpif_linux_vport_init(&request);
     request.cmd = OVS_VPORT_CMD_GET;
     request.dp_ifindex = dpif->dp_ifindex;
 
     buf = ofpbuf_new(1024);
     dpif_linux_vport_to_ofpbuf(&request, buf);
-    nl_dump_start(&state->dump, NETLINK_GENERIC, buf);
+    nl_dump_start(dump, NETLINK_GENERIC, buf);
     ofpbuf_delete(buf);
+}
+
+static int
+dpif_linux_port_dump_start(const struct dpif *dpif, void **statep)
+{
+    struct dpif_linux_port_state *state;
+
+    *statep = state = xmalloc(sizeof *state);
+    dpif_linux_port_dump_start__(dpif, &state->dump);
 
     return 0;
 }
 
 static int
-dpif_linux_port_dump_next(const struct dpif *dpif OVS_UNUSED, void *state_,
-                          struct dpif_port *dpif_port)
+dpif_linux_port_dump_next__(const struct dpif *dpif_, struct nl_dump *dump,
+                            struct dpif_linux_vport *vport)
 {
-    struct dpif_linux_port_state *state = state_;
-    struct dpif_linux_vport vport;
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
     struct ofpbuf buf;
     int error;
 
-    if (!nl_dump_next(&state->dump, &buf)) {
+    if (!nl_dump_next(dump, &buf)) {
         return EOF;
     }
 
-    error = dpif_linux_vport_from_ofpbuf(&vport, &buf);
+    error = dpif_linux_vport_from_ofpbuf(vport, &buf);
     if (error) {
-        return error;
+        VLOG_WARN_RL(&error_rl, "%s: failed to parse vport record (%s)",
+                     dpif_name(&dpif->dpif), ovs_strerror(error));
     }
+    return error;
+}
 
+static int
+dpif_linux_port_dump_next(const struct dpif *dpif OVS_UNUSED, void *state_,
+                          struct dpif_port *dpif_port)
+{
+    struct dpif_linux_port_state *state = state_;
+    struct dpif_linux_vport vport;
+    int error;
+
+    error = dpif_linux_port_dump_next__(dpif, &state->dump, &vport);
+    if (error) {
+        return error;
+    }
     dpif_port->name = CONST_CAST(char *, vport.name);
     dpif_port->type = CONST_CAST(char *, get_vport_type(&vport));
     dpif_port->port_no = vport.port_no;
@@ -801,6 +837,9 @@ dpif_linux_port_poll(const struct dpif *dpif_, char **devnamep)
                         || vport.cmd == OVS_VPORT_CMD_SET)) {
                     VLOG_DBG("port_changed: dpif:%s vport:%s cmd:%"PRIu8,
                              dpif->dpif.full_name, vport.name, vport.cmd);
+                    if (vport.cmd == OVS_VPORT_CMD_DEL) {
+                        dpif->refresh_channels = true;
+                    }
                     *devnamep = xstrdup(vport.name);
                     ofpbuf_uninit(&buf);
                     return 0;
@@ -1051,10 +1090,11 @@ dpif_linux_encode_execute(int dp_ifindex, const struct dpif_execute *d_exec,
                           struct ofpbuf *buf)
 {
     struct ovs_header *k_exec;
+    size_t key_ofs;
 
     ofpbuf_prealloc_tailroom(buf, (64
                                    + d_exec->packet->size
-                                   + d_exec->key_len
+                                   + ODP_KEY_METADATA_SIZE
                                    + d_exec->actions_len));
 
     nl_msg_put_genlmsghdr(buf, 0, ovs_packet_family, NLM_F_REQUEST,
@@ -1065,7 +1105,11 @@ dpif_linux_encode_execute(int dp_ifindex, const struct dpif_execute *d_exec,
 
     nl_msg_put_unspec(buf, OVS_PACKET_ATTR_PACKET,
                       d_exec->packet->data, d_exec->packet->size);
-    nl_msg_put_unspec(buf, OVS_PACKET_ATTR_KEY, d_exec->key, d_exec->key_len);
+
+    key_ofs = nl_msg_start_nested(buf, OVS_PACKET_ATTR_KEY);
+    odp_key_from_pkt_metadata(buf, &d_exec->md);
+    nl_msg_end_nested(buf, key_ofs);
+
     nl_msg_put_unspec(buf, OVS_PACKET_ATTR_ACTIONS,
                       d_exec->actions, d_exec->actions_len);
 }
@@ -1086,7 +1130,7 @@ dpif_linux_execute__(int dp_ifindex, const struct dpif_execute *execute)
 }
 
 static int
-dpif_linux_execute(struct dpif *dpif_, const struct dpif_execute *execute)
+dpif_linux_execute(struct dpif *dpif_, struct dpif_execute *execute)
 {
     const struct dpif_linux *dpif = dpif_linux_cast(dpif_);
 
@@ -1157,7 +1201,7 @@ dpif_linux_operate__(struct dpif *dpif_, struct dpif_op **ops, size_t n_ops)
             break;
 
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
 
@@ -1218,7 +1262,7 @@ dpif_linux_operate__(struct dpif *dpif_, struct dpif_op **ops, size_t n_ops)
             break;
 
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
 
         ofpbuf_uninit(&aux->request);
@@ -1237,45 +1281,59 @@ dpif_linux_operate(struct dpif *dpif, struct dpif_op **ops, size_t n_ops)
     }
 }
 
+/* Synchronizes 'dpif->channels' with the set of vports currently in 'dpif' in
+ * the kernel, by adding a new channel for any kernel vport that lacks one and
+ * deleting any channels that have no backing kernel vports. */
 static int
-dpif_linux_recv_set__(struct dpif *dpif_, bool enable)
+dpif_linux_refresh_channels(struct dpif *dpif_)
 {
     struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+    unsigned long int *keep_channels;
+    struct dpif_linux_vport vport;
+    size_t keep_channels_nbits;
+    struct nl_dump dump;
+    int retval = 0;
+    size_t i;
 
-    if ((dpif->epoll_fd >= 0) == enable) {
-        return 0;
+    /* To start with, we need an epoll fd. */
+    if (dpif->epoll_fd < 0) {
+        dpif->epoll_fd = epoll_create(10);
+        if (dpif->epoll_fd < 0) {
+            return errno;
+        }
     }
 
-    if (!enable) {
-        destroy_channels(dpif);
-    } else {
-        struct dpif_port_dump port_dump;
-        struct dpif_port port;
+    keep_channels_nbits = dpif->uc_array_size;
+    keep_channels = bitmap_allocate(keep_channels_nbits);
 
-        if (dpif->epoll_fd < 0) {
-            dpif->epoll_fd = epoll_create(10);
-            if (dpif->epoll_fd < 0) {
-                return errno;
-            }
-        }
+    dpif->n_events = dpif->event_offset = 0;
 
-        DPIF_PORT_FOR_EACH (&port, &port_dump, &dpif->dpif) {
-            struct dpif_linux_vport vport_request;
-            struct nl_sock *sock;
-            uint32_t upcall_pid;
-            int error;
+    dpif_linux_port_dump_start__(dpif_, &dump);
+    while (!dpif_linux_port_dump_next__(dpif_, &dump, &vport)) {
+        uint32_t port_no = odp_to_u32(vport.port_no);
+        struct nl_sock *sock = (port_no < dpif->uc_array_size
+                                ? dpif->channels[port_no].sock
+                                : NULL);
+        bool new_sock = !sock;
+        int error;
 
+        if (new_sock) {
             error = nl_sock_create(NETLINK_GENERIC, &sock);
             if (error) {
-                return error;
+                retval = error;
+                goto error;
             }
+        }
 
-            upcall_pid = nl_sock_pid(sock);
+        /* Configure the vport to deliver misses to 'sock'. */
+        if (!vport.upcall_pid || *vport.upcall_pid != nl_sock_pid(sock)) {
+            uint32_t upcall_pid = nl_sock_pid(sock);
+            struct dpif_linux_vport vport_request;
 
             dpif_linux_vport_init(&vport_request);
             vport_request.cmd = OVS_VPORT_CMD_SET;
             vport_request.dp_ifindex = dpif->dp_ifindex;
-            vport_request.port_no = port.port_no;
+            vport_request.port_no = vport.port_no;
             vport_request.upcall_pid = &upcall_pid;
             error = dpif_linux_vport_transact(&vport_request, NULL, NULL);
             if (!error) {
@@ -1286,27 +1344,63 @@ dpif_linux_recv_set__(struct dpif *dpif_, bool enable)
                 VLOG_WARN_RL(&error_rl,
                              "%s: failed to set upcall pid on port: %s",
                              dpif_name(&dpif->dpif), ovs_strerror(error));
-                nl_sock_destroy(sock);
 
-                if (error == ENODEV || error == ENOENT) {
-                    /* This device isn't there, but keep trying the others. */
-                    continue;
+                if (error != ENODEV && error != ENOENT) {
+                    retval = error;
                 } else {
-                    return error;
+                    /* The vport isn't really there, even though the dump says
+                     * it is.  Probably we just hit a race after a port
+                     * disappeared. */
                 }
+                goto error;
             }
+        }
 
-            error = add_channel(dpif, port.port_no, sock);
+        if (new_sock) {
+            error = add_channel(dpif, vport.port_no, sock);
             if (error) {
                 VLOG_INFO("%s: could not add channel for port %s",
-                          dpif_name(dpif_), port.name);
-                nl_sock_destroy(sock);
-                return error;
+                          dpif_name(dpif_), vport.name);
+                retval = error;
+                goto error;
             }
         }
+
+        if (port_no < keep_channels_nbits) {
+            bitmap_set1(keep_channels, port_no);
+        }
+        continue;
+
+    error:
+        nl_sock_destroy(sock);
     }
+    nl_dump_done(&dump);
 
-    return 0;
+    /* Discard any saved channels that we didn't reuse. */
+    for (i = 0; i < keep_channels_nbits; i++) {
+        if (!bitmap_is_set(keep_channels, i)) {
+            nl_sock_destroy(dpif->channels[i].sock);
+            dpif->channels[i].sock = NULL;
+        }
+    }
+    free(keep_channels);
+
+    return retval;
+}
+
+static int
+dpif_linux_recv_set__(struct dpif *dpif_, bool enable)
+{
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+
+    if ((dpif->epoll_fd >= 0) == enable) {
+        return 0;
+    } else if (!enable) {
+        destroy_channels(dpif);
+        return 0;
+    } else {
+        return dpif_linux_refresh_channels(dpif_);
+    }
 }
 
 static int
@@ -1374,16 +1468,22 @@ parse_odp_packet(struct ofpbuf *buf, struct dpif_upcall *upcall,
         return EINVAL;
     }
 
-    memset(upcall, 0, sizeof *upcall);
+    /* (Re)set ALL fields of '*upcall' on successful return. */
     upcall->type = type;
-    upcall->packet = buf;
-    upcall->packet->data = CONST_CAST(struct nlattr *,
-                                      nl_attr_get(a[OVS_PACKET_ATTR_PACKET]));
-    upcall->packet->size = nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET]);
     upcall->key = CONST_CAST(struct nlattr *,
                              nl_attr_get(a[OVS_PACKET_ATTR_KEY]));
     upcall->key_len = nl_attr_get_size(a[OVS_PACKET_ATTR_KEY]);
     upcall->userdata = a[OVS_PACKET_ATTR_USERDATA];
+
+    /* Allow overwriting the netlink attribute header without reallocating. */
+    ofpbuf_use_stub(&upcall->packet,
+                    CONST_CAST(struct nlattr *,
+                               nl_attr_get(a[OVS_PACKET_ATTR_PACKET])) - 1,
+                    nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET]) +
+                    sizeof(struct nlattr));
+    upcall->packet.data = (char *)upcall->packet.data + sizeof(struct nlattr);
+    upcall->packet.size = nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET]);
+
     *dp_ifindex = ovs_header->dp_ifindex;
 
     return 0;
@@ -1513,7 +1613,7 @@ const struct dpif_class dpif_linux_class = {
     dpif_linux_open,
     dpif_linux_close,
     dpif_linux_destroy,
-    NULL,                       /* run */
+    dpif_linux_run,
     NULL,                       /* wait */
     dpif_linux_get_stats,
     dpif_linux_port_add,
@@ -1839,6 +1939,10 @@ dpif_linux_dp_to_ofpbuf(const struct dpif_linux_dp *dp, struct ofpbuf *buf)
         nl_msg_put_u32(buf, OVS_DP_ATTR_UPCALL_PID, *dp->upcall_pid);
     }
 
+    if (dp->user_features) {
+        nl_msg_put_u32(buf, OVS_DP_ATTR_USER_FEATURES, dp->user_features);
+    }
+
     /* Skip OVS_DP_ATTR_STATS since we never have a reason to serialize it. */
 }
 
index 11f010c..0de6027 100644 (file)
@@ -40,6 +40,7 @@
 #include "flow.h"
 #include "hmap.h"
 #include "list.h"
+#include "meta-flow.h"
 #include "netdev.h"
 #include "netdev-vport.h"
 #include "netlink.h"
@@ -165,17 +166,15 @@ static int do_add_port(struct dp_netdev *, const char *devname,
 static int do_del_port(struct dp_netdev *, odp_port_t port_no);
 static int dpif_netdev_open(const struct dpif_class *, const char *name,
                             bool create, struct dpif **);
-static int dp_netdev_output_userspace(struct dp_netdev *, const struct ofpbuf *,
+static int dp_netdev_output_userspace(struct dp_netdev *, struct ofpbuf *,
                                     int queue_no, const struct flow *,
                                     const struct nlattr *userdata);
 static void dp_netdev_execute_actions(struct dp_netdev *, const struct flow *,
-                                      struct ofpbuf *,
+                                      struct ofpbuf *, struct pkt_metadata *,
                                       const struct nlattr *actions,
                                       size_t actions_len);
-static void dp_netdev_port_input(struct dp_netdev *dp,
-                                 struct dp_netdev_port *port,
-                                 struct ofpbuf *packet, uint32_t skb_priority,
-                                 uint32_t pkt_mark, const struct flow_tnl *tnl);
+static void dp_netdev_port_input(struct dp_netdev *dp, struct ofpbuf *packet,
+                                 struct pkt_metadata *md);
 
 static struct dpif_netdev *
 dpif_netdev_cast(const struct dpif *dpif)
@@ -351,6 +350,7 @@ dp_netdev_purge_queues(struct dp_netdev *dp)
 
         while (q->tail != q->head) {
             struct dp_netdev_upcall *u = &q->upcalls[q->tail++ & QUEUE_MASK];
+            ofpbuf_uninit(&u->upcall.packet);
             ofpbuf_uninit(&u->buf);
         }
     }
@@ -413,7 +413,7 @@ dpif_netdev_get_stats(const struct dpif *dpif, struct dpif_dp_stats *stats)
     stats->n_hit = dp->n_hit;
     stats->n_missed = dp->n_missed;
     stats->n_lost = dp->n_lost;
-    stats->n_masks = UINT64_MAX;
+    stats->n_masks = UINT32_MAX;
     stats->n_mask_hit = UINT64_MAX;
     ovs_mutex_unlock(&dp_netdev_mutex);
 
@@ -796,29 +796,72 @@ get_dpif_flow_stats(struct dp_netdev_flow *netdev_flow,
 }
 
 static int
-dpif_netdev_flow_mask_from_nlattrs(const struct nlattr *key, uint32_t key_len,
-                                   const struct nlattr *mask_key,
-                                   uint32_t mask_key_len, struct flow *flow,
-                                   struct flow *mask)
+dpif_netdev_mask_from_nlattrs(const struct nlattr *key, uint32_t key_len,
+                              const struct nlattr *mask_key,
+                              uint32_t mask_key_len, const struct flow *flow,
+                              struct flow *mask)
+{
+    if (mask_key_len) {
+        if (odp_flow_key_to_mask(mask_key, mask_key_len, mask, flow)) {
+            /* This should not happen: it indicates that
+             * odp_flow_key_from_mask() and odp_flow_key_to_mask()
+             * disagree on the acceptable form of a mask.  Log the problem
+             * as an error, with enough details to enable debugging. */
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+            if (!VLOG_DROP_ERR(&rl)) {
+                struct ds s;
+
+                ds_init(&s);
+                odp_flow_format(key, key_len, mask_key, mask_key_len, NULL, &s,
+                                true);
+                VLOG_ERR("internal error parsing flow mask %s", ds_cstr(&s));
+                ds_destroy(&s);
+            }
+
+            return EINVAL;
+        }
+        /* Force unwildcard the in_port. */
+        mask->in_port.odp_port = u32_to_odp(UINT32_MAX);
+    } else {
+        enum mf_field_id id;
+        /* No mask key, unwildcard everything except fields whose
+         * prerequisities are not met. */
+        memset(mask, 0x0, sizeof *mask);
+
+        for (id = 0; id < MFF_N_IDS; ++id) {
+            /* Skip registers and metadata. */
+            if (!(id >= MFF_REG0 && id < MFF_REG0 + FLOW_N_REGS)
+                && id != MFF_METADATA) {
+                const struct mf_field *mf = mf_from_id(id);
+                if (mf_are_prereqs_ok(mf, flow)) {
+                    mf_mask_field(mf, mask);
+                }
+            }
+        }
+    }
+
+    return 0;
+}
+
+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)
-        || (mask_key
-            && odp_flow_key_to_mask(mask_key, mask_key_len, mask, flow))) {
+    if (odp_flow_key_to_flow(key, key_len, flow)) {
         /* 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 flow
-         * or odp_flow_key_from_mask() and odp_flow_key_to_mask() disagree on
-         * the acceptable form of a mask.  Log the problem as an error, with
-         * enough details to enable debugging. */
+         * and odp_flow_key_to_flow() disagree on the acceptable form of a
+         * flow.  Log the problem as an error, with enough details to enable
+         * debugging. */
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
         if (!VLOG_DROP_ERR(&rl)) {
             struct ds s;
 
             ds_init(&s);
-            odp_flow_format(key, key_len, mask_key, mask_key_len, NULL, &s,
-                            true);
+            odp_flow_format(key, key_len, NULL, 0, NULL, &s, true);
             VLOG_ERR("internal error parsing flow key %s", ds_cstr(&s));
             ds_destroy(&s);
         }
@@ -826,11 +869,6 @@ dpif_netdev_flow_mask_from_nlattrs(const struct nlattr *key, uint32_t key_len,
         return EINVAL;
     }
 
-    if (mask_key) {
-        /* Force unwildcard the in_port. */
-        mask->in_port.odp_port = u32_to_odp(UINT32_MAX);
-    }
-
     in_port = flow->in_port.odp_port;
     if (!is_valid_port_number(in_port) && in_port != ODPP_NONE) {
         return EINVAL;
@@ -839,14 +877,6 @@ dpif_netdev_flow_mask_from_nlattrs(const struct nlattr *key, uint32_t key_len,
     return 0;
 }
 
-static int
-dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len,
-                              struct flow *flow)
-{
-    return dpif_netdev_flow_mask_from_nlattrs(key, key_len, NULL, 0, flow,
-                                              NULL);
-}
-
 static int
 dpif_netdev_flow_get(const struct dpif *dpif,
                      const struct nlattr *nl_key, size_t nl_key_len,
@@ -942,8 +972,13 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put)
     struct flow_wildcards wc;
     int error;
 
-    error = dpif_netdev_flow_mask_from_nlattrs(put->key, put->key_len,
-                put->mask, put->mask_len, &flow, &wc.masks);
+    error = dpif_netdev_flow_from_nlattrs(put->key, put->key_len, &flow);
+    if (error) {
+        return error;
+    }
+    error = dpif_netdev_mask_from_nlattrs(put->key, put->key_len,
+                                          put->mask, put->mask_len,
+                                          &flow, &wc.masks);
     if (error) {
         return error;
     }
@@ -1112,36 +1147,25 @@ dpif_netdev_flow_dump_done(const struct dpif *dpif OVS_UNUSED, void *state_)
 }
 
 static int
-dpif_netdev_execute(struct dpif *dpif, const struct dpif_execute *execute)
+dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
-    struct flow md;
-    int error;
+    struct pkt_metadata *md = &execute->md;
+    struct flow key;
 
     if (execute->packet->size < ETH_HEADER_LEN ||
         execute->packet->size > UINT16_MAX) {
         return EINVAL;
     }
 
-    /* Get packet metadata. */
-    error = dpif_netdev_flow_from_nlattrs(execute->key, execute->key_len, &md);
-    if (!error) {
-        struct ofpbuf *copy;
-        struct flow key;
-
-        /* Make a deep copy of 'packet', because we might modify its data. */
-        copy = ofpbuf_clone_with_headroom(execute->packet, DP_NETDEV_HEADROOM);
-
-        /* Extract flow key. */
-        flow_extract(copy, md.skb_priority, md.pkt_mark, &md.tunnel,
-                     &md.in_port, &key);
-        ovs_mutex_lock(&dp_netdev_mutex);
-        dp_netdev_execute_actions(dp, &key, copy,
-                                  execute->actions, execute->actions_len);
-        ovs_mutex_unlock(&dp_netdev_mutex);
-        ofpbuf_delete(copy);
-    }
-    return error;
+    /* Extract flow key. */
+    flow_extract(execute->packet, md->skb_priority, md->pkt_mark, &md->tunnel,
+                 (union flow_in_port *)&md->in_port, &key);
+    ovs_mutex_lock(&dp_netdev_mutex);
+    dp_netdev_execute_actions(dp, &key, execute->packet, md, execute->actions,
+                              execute->actions_len);
+    ovs_mutex_unlock(&dp_netdev_mutex);
+    return 0;
 }
 
 static int
@@ -1186,7 +1210,6 @@ dpif_netdev_recv(struct dpif *dpif, struct dpif_upcall *upcall,
         struct dp_netdev_upcall *u = &q->upcalls[q->tail++ & QUEUE_MASK];
 
         *upcall = u->upcall;
-        upcall->packet = buf;
 
         ofpbuf_uninit(buf);
         *buf = u->buf;
@@ -1236,23 +1259,21 @@ dp_netdev_flow_used(struct dp_netdev_flow *netdev_flow,
 }
 
 static void
-dp_netdev_port_input(struct dp_netdev *dp, struct dp_netdev_port *port,
-                     struct ofpbuf *packet, uint32_t skb_priority,
-                     uint32_t pkt_mark, const struct flow_tnl *tnl)
+dp_netdev_port_input(struct dp_netdev *dp, struct ofpbuf *packet,
+                     struct pkt_metadata *md)
 {
     struct dp_netdev_flow *netdev_flow;
     struct flow key;
-    union flow_in_port in_port_;
 
     if (packet->size < ETH_HEADER_LEN) {
         return;
     }
-    in_port_.odp_port = port->port_no;
-    flow_extract(packet, skb_priority, pkt_mark, tnl, &in_port_, &key);
+    flow_extract(packet, md->skb_priority, md->pkt_mark, &md->tunnel,
+                 (union flow_in_port *)&md->in_port, &key);
     netdev_flow = dp_netdev_lookup_flow(dp, &key);
     if (netdev_flow) {
         dp_netdev_flow_used(netdev_flow, packet);
-        dp_netdev_execute_actions(dp, &key, packet,
+        dp_netdev_execute_actions(dp, &key, packet, md,
                                   netdev_flow->actions,
                                   netdev_flow->actions_len);
         dp->n_hit++;
@@ -1268,22 +1289,25 @@ dpif_netdev_run(struct dpif *dpif)
     struct dp_netdev_port *port;
     struct dp_netdev *dp;
     struct ofpbuf packet;
+    size_t buf_size;
 
     ovs_mutex_lock(&dp_netdev_mutex);
     dp = get_dp_netdev(dpif);
-    ofpbuf_init(&packet,
-                DP_NETDEV_HEADROOM + VLAN_ETH_HEADER_LEN + dp->max_mtu);
+    ofpbuf_init(&packet, 0);
+
+    buf_size = DP_NETDEV_HEADROOM + VLAN_ETH_HEADER_LEN + dp->max_mtu;
 
     LIST_FOR_EACH (port, node, &dp->port_list) {
         int error;
 
-        /* Reset packet contents. */
+        /* Reset packet contents. Packet data may have been stolen. */
         ofpbuf_clear(&packet);
-        ofpbuf_reserve(&packet, DP_NETDEV_HEADROOM);
+        ofpbuf_reserve_with_tailroom(&packet, DP_NETDEV_HEADROOM, buf_size);
 
         error = port->rx ? netdev_rx_recv(port->rx, &packet) : EOPNOTSUPP;
         if (!error) {
-            dp_netdev_port_input(dp, port, &packet, 0, 0, NULL);
+            struct pkt_metadata md = PKT_METADATA_INITIALIZER(port->port_no);
+            dp_netdev_port_input(dp, &packet, &md);
         } else if (error != EAGAIN && error != EOPNOTSUPP) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
@@ -1322,8 +1346,18 @@ dpif_netdev_wait(struct dpif *dpif)
     ovs_mutex_unlock(&dp_netdev_mutex);
 }
 
+static void
+dp_netdev_output_port(struct dp_netdev *dp, struct ofpbuf *packet,
+                      odp_port_t out_port)
+{
+    struct dp_netdev_port *p = dp->ports[odp_to_u32(out_port)];
+    if (p) {
+        netdev_send(p->netdev, packet);
+    }
+}
+
 static int
-dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet,
+dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf *packet,
                            int queue_no, const struct flow *flow,
                            const struct nlattr *userdata)
 {
@@ -1337,7 +1371,7 @@ dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet,
         upcall->type = queue_no;
 
         /* Allocate buffer big enough for everything. */
-        buf_size = ODPUTIL_FLOW_KEY_BYTES + 2 + packet->size;
+        buf_size = ODPUTIL_FLOW_KEY_BYTES;
         if (userdata) {
             buf_size += NLA_ALIGN(userdata->nla_len);
         }
@@ -1354,15 +1388,10 @@ dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet,
                                           NLA_ALIGN(userdata->nla_len));
         }
 
-        /* Put packet.
-         *
-         * We adjust 'data' and 'size' in 'buf' so that only the packet itself
-         * is visible in 'upcall->packet'.  The ODP flow and (if present)
-         * userdata become part of the headroom. */
-        ofpbuf_put_zeros(buf, 2);
-        buf->data = ofpbuf_put(buf, packet->data, packet->size);
-        buf->size = packet->size;
-        upcall->packet = buf;
+        /* Steal packet data. */
+        ovs_assert(packet->source == OFPBUF_MALLOC);
+        upcall->packet = *packet;
+        ofpbuf_use(packet, NULL, 0);
 
         seq_change(dp->queue_seq);
 
@@ -1379,40 +1408,54 @@ struct dp_netdev_execute_aux {
 };
 
 static void
-dp_netdev_action_output(void *aux_, struct ofpbuf *packet,
-                        const struct flow *flow OVS_UNUSED,
-                        odp_port_t out_port)
+dp_execute_cb(void *aux_, struct ofpbuf *packet,
+              const struct pkt_metadata *md OVS_UNUSED,
+              const struct nlattr *a, bool may_steal)
 {
     struct dp_netdev_execute_aux *aux = aux_;
-    struct dp_netdev_port *p = aux->dp->ports[odp_to_u32(out_port)];
-    if (p) {
-        netdev_send(p->netdev, packet);
-    }
-}
+    int type = nl_attr_type(a);
 
-static void
-dp_netdev_action_userspace(void *aux_, struct ofpbuf *packet,
-                           const struct flow *flow OVS_UNUSED,
-                           const struct nlattr *a)
-{
-    struct dp_netdev_execute_aux *aux = aux_;
-    const struct nlattr *userdata;
+    switch ((enum ovs_action_attr)type) {
+    case OVS_ACTION_ATTR_OUTPUT:
+        dp_netdev_output_port(aux->dp, packet, u32_to_odp(nl_attr_get_u32(a)));
+        break;
+
+    case OVS_ACTION_ATTR_USERSPACE: {
+        const struct nlattr *userdata;
 
-    userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
-    dp_netdev_output_userspace(aux->dp, packet, DPIF_UC_ACTION, aux->key,
-                               userdata);
+        userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
+
+        /* Make a copy if we are not allowed to steal the packet's data. */
+        if (!may_steal) {
+            packet = ofpbuf_clone_with_headroom(packet, DP_NETDEV_HEADROOM);
+        }
+        dp_netdev_output_userspace(aux->dp, packet, DPIF_UC_ACTION, aux->key,
+                                   userdata);
+        if (!may_steal) {
+            ofpbuf_uninit(packet);
+        }
+        break;
+    }
+    case OVS_ACTION_ATTR_PUSH_VLAN:
+    case OVS_ACTION_ATTR_POP_VLAN:
+    case OVS_ACTION_ATTR_PUSH_MPLS:
+    case OVS_ACTION_ATTR_POP_MPLS:
+    case OVS_ACTION_ATTR_SET:
+    case OVS_ACTION_ATTR_SAMPLE:
+    case OVS_ACTION_ATTR_UNSPEC:
+    case __OVS_ACTION_ATTR_MAX:
+        OVS_NOT_REACHED();
+    }
 }
 
 static void
 dp_netdev_execute_actions(struct dp_netdev *dp, const struct flow *key,
-                          struct ofpbuf *packet,
+                          struct ofpbuf *packet, struct pkt_metadata *md,
                           const struct nlattr *actions, size_t actions_len)
 {
     struct dp_netdev_execute_aux aux = {dp, key};
-    struct flow md = *key;   /* Packet metadata, may be modified by actions. */
 
-    odp_execute_actions(&aux, packet, &md, actions, actions_len,
-                        dp_netdev_action_output, dp_netdev_action_userspace);
+    odp_execute_actions(&aux, packet, md, actions, actions_len, dp_execute_cb);
 }
 
 #define DPIF_NETDEV_CLASS_FUNCTIONS                    \
index f85a658..522a31d 100644 (file)
@@ -311,12 +311,10 @@ struct dpif_class {
     int (*flow_dump_done)(const struct dpif *dpif, void *state);
 
     /* Performs the 'execute->actions_len' bytes of actions in
-     * 'execute->actions' on the Ethernet frame specified in 'execute->packet'
-     * taken from the flow specified in the 'execute->key_len' bytes of
-     * 'execute->key'.  ('execute->key' is mostly redundant with
-     * 'execute->packet', but it contains some metadata that cannot be
-     * recovered from 'execute->packet', such as tunnel and in_port.) */
-    int (*execute)(struct dpif *dpif, const struct dpif_execute *execute);
+     * 'execute->actions' on the Ethernet frame in 'execute->packet'
+     * and on the packet metadata in 'execute->md'.
+     * May modify both packet and metadata. */
+    int (*execute)(struct dpif *dpif, struct dpif_execute *execute);
 
     /* Executes each of the 'n_ops' operations in 'ops' on 'dpif', in the order
      * in which they are specified, placing each operation's results in the
@@ -341,11 +339,17 @@ struct dpif_class {
      * '*upcall', using 'buf' for storage.  Should only be called if 'recv_set'
      * has been used to enable receiving packets from 'dpif'.
      *
-     * The implementation should point 'upcall->packet' and 'upcall->key' into
-     * data in the caller-provided 'buf'.  If necessary to make room, the
-     * implementation may expand the data in 'buf'.  (This is hardly a great
-     * way to do things but it works out OK for the dpif providers that exist
-     * so far.)
+     * The implementation should point 'upcall->key' and 'upcall->userdata'
+     * (if any) into data in the caller-provided 'buf'.  The implementation may
+     * also use 'buf' for storing the data of 'upcall->packet'.  If necessary
+     * to make room, the implementation may reallocate the data in 'buf'.
+     *
+     * The caller owns the data of 'upcall->packet' and may modify it.  If
+     * packet's headroom is exhausted as it is manipulated, 'upcall->packet'
+     * will be reallocated.  This requires the data of 'upcall->packet' to be
+     * released with ofpbuf_uninit() before 'upcall' is destroyed.  However,
+     * when an error is returned, the 'upcall->packet' may be uninitialized
+     * and should not be released.
      *
      * This function must not block.  If no upcall is pending when it is
      * called, it should return EAGAIN without blocking. */
index 7151ef9..aa27d62 100644 (file)
@@ -1065,97 +1065,82 @@ struct dpif_execute_helper_aux {
     int error;
 };
 
+/* This is called for actions that need the context of the datapath to be
+ * meaningful. */
 static void
-dpif_execute_helper_execute__(void *aux_, struct ofpbuf *packet,
-                              const struct flow *flow,
-                              const struct nlattr *actions, size_t actions_len)
+dpif_execute_helper_cb(void *aux_, struct ofpbuf *packet,
+                       const struct pkt_metadata *md,
+                       const struct nlattr *action, bool may_steal OVS_UNUSED)
 {
     struct dpif_execute_helper_aux *aux = aux_;
     struct dpif_execute execute;
-    struct odputil_keybuf key_stub;
-    struct ofpbuf key;
-    int error;
-
-    ofpbuf_use_stub(&key, &key_stub, sizeof key_stub);
-    odp_flow_key_from_flow(&key, flow, flow->in_port.odp_port);
-
-    execute.key = key.data;
-    execute.key_len = key.size;
-    execute.actions = actions;
-    execute.actions_len = actions_len;
-    execute.packet = packet;
-    execute.needs_help = false;
-
-    error = aux->dpif->dpif_class->execute(aux->dpif, &execute);
-    if (error) {
-        aux->error = error;
+    int type = nl_attr_type(action);
+
+    switch ((enum ovs_action_attr)type) {
+    case OVS_ACTION_ATTR_OUTPUT:
+    case OVS_ACTION_ATTR_USERSPACE:
+        execute.actions = action;
+        execute.actions_len = NLA_ALIGN(action->nla_len);
+        execute.packet = packet;
+        execute.md = *md;
+        execute.needs_help = false;
+        aux->error = aux->dpif->dpif_class->execute(aux->dpif, &execute);
+        break;
+
+    case OVS_ACTION_ATTR_PUSH_VLAN:
+    case OVS_ACTION_ATTR_POP_VLAN:
+    case OVS_ACTION_ATTR_PUSH_MPLS:
+    case OVS_ACTION_ATTR_POP_MPLS:
+    case OVS_ACTION_ATTR_SET:
+    case OVS_ACTION_ATTR_SAMPLE:
+    case OVS_ACTION_ATTR_UNSPEC:
+    case __OVS_ACTION_ATTR_MAX:
+        OVS_NOT_REACHED();
     }
 }
 
-static void
-dpif_execute_helper_output_cb(void *aux, struct ofpbuf *packet,
-                              const struct flow *flow, odp_port_t out_port)
-{
-    uint64_t actions_stub[DIV_ROUND_UP(NL_A_U32_SIZE, 8)];
-    struct ofpbuf actions;
-
-    ofpbuf_use_stack(&actions, actions_stub, sizeof actions_stub);
-    nl_msg_put_u32(&actions, OVS_ACTION_ATTR_OUTPUT, odp_to_u32(out_port));
-
-    dpif_execute_helper_execute__(aux, packet, flow,
-                                  actions.data, actions.size);
-}
-
-static void
-dpif_execute_helper_userspace_cb(void *aux, struct ofpbuf *packet,
-                                 const struct flow *flow,
-                                 const struct nlattr *action)
-{
-    dpif_execute_helper_execute__(aux, packet, flow,
-                                  action, NLA_ALIGN(action->nla_len));
-}
-
 /* Executes 'execute' by performing most of the actions in userspace and
  * passing the fully constructed packets to 'dpif' for output and userspace
  * actions.
  *
  * This helps with actions that a given 'dpif' doesn't implement directly. */
 static int
-dpif_execute_with_help(struct dpif *dpif, const struct dpif_execute *execute)
+dpif_execute_with_help(struct dpif *dpif, struct dpif_execute *execute)
 {
-    struct dpif_execute_helper_aux aux;
-    enum odp_key_fitness fit;
-    struct ofpbuf *packet;
-    struct flow flow;
+    struct dpif_execute_helper_aux aux = {dpif, 0};
 
     COVERAGE_INC(dpif_execute_with_help);
 
-    fit = odp_flow_key_to_flow(execute->key, execute->key_len, &flow);
-    if (fit == ODP_FIT_ERROR) {
-        return EINVAL;
-    }
-
-    aux.dpif = dpif;
-    aux.error = 0;
-
-    packet = ofpbuf_clone_with_headroom(execute->packet, VLAN_HEADER_LEN);
-    odp_execute_actions(&aux, packet, &flow,
+    odp_execute_actions(&aux, execute->packet, &execute->md,
                         execute->actions, execute->actions_len,
-                        dpif_execute_helper_output_cb,
-                        dpif_execute_helper_userspace_cb);
-    ofpbuf_delete(packet);
-
+                        dpif_execute_helper_cb);
     return aux.error;
 }
 
-static int
-dpif_execute__(struct dpif *dpif, const struct dpif_execute *execute)
+/* Causes 'dpif' to perform the 'execute->actions_len' bytes of actions in
+ * 'execute->actions' on the Ethernet frame in 'execute->packet' and on packet
+ * metadata in 'execute->md'.  The implementation is allowed to modify both the
+ * '*execute->packet' and 'execute->md'.
+ *
+ * Some dpif providers do not implement every action.  The Linux kernel
+ * datapath, in particular, does not implement ARP field modification.  If
+ * 'needs_help' is true, the dpif layer executes in userspace all of the
+ * actions that it can, and for OVS_ACTION_ATTR_OUTPUT and
+ * OVS_ACTION_ATTR_USERSPACE actions it passes the packet through to the dpif
+ * implementation.
+ *
+ * This works even if 'execute->actions_len' is too long for a Netlink
+ * attribute.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
+int
+dpif_execute(struct dpif *dpif, struct dpif_execute *execute)
 {
     int error;
 
     COVERAGE_INC(dpif_execute);
     if (execute->actions_len > 0) {
-        error = (execute->needs_help
+        error = (execute->needs_help || nl_attr_oversized(execute->actions_len)
                  ? dpif_execute_with_help(dpif, execute)
                  : dpif->dpif_class->execute(dpif, execute));
     } else {
@@ -1167,40 +1152,6 @@ dpif_execute__(struct dpif *dpif, const struct dpif_execute *execute)
     return error;
 }
 
-/* Causes 'dpif' to perform the 'actions_len' bytes of actions in 'actions' on
- * the Ethernet frame specified in 'packet' taken from the flow specified in
- * the 'key_len' bytes of 'key'.  ('key' is mostly redundant with 'packet', but
- * it contains some metadata that cannot be recovered from 'packet', such as
- * tunnel and in_port.)
- *
- * Some dpif providers do not implement every action.  The Linux kernel
- * datapath, in particular, does not implement ARP field modification.  If
- * 'needs_help' is true, the dpif layer executes in userspace all of the
- * actions that it can, and for OVS_ACTION_ATTR_OUTPUT and
- * OVS_ACTION_ATTR_USERSPACE actions it passes the packet through to the dpif
- * implementation.
- *
- * This works even if 'actions_len' is too long for a Netlink attribute.
- *
- * Returns 0 if successful, otherwise a positive errno value. */
-int
-dpif_execute(struct dpif *dpif,
-             const struct nlattr *key, size_t key_len,
-             const struct nlattr *actions, size_t actions_len,
-             const struct ofpbuf *buf,
-             bool needs_help)
-{
-    struct dpif_execute execute;
-
-    execute.key = key;
-    execute.key_len = key_len;
-    execute.actions = actions;
-    execute.actions_len = actions_len;
-    execute.packet = buf;
-    execute.needs_help = needs_help || nl_attr_oversized(actions_len);
-    return dpif_execute__(dpif, &execute);
-}
-
 /* Executes each of the 'n_ops' operations in 'ops' on 'dpif', in the order in
  * which they are specified, placing each operation's results in the "output"
  * members documented in comments.
@@ -1256,7 +1207,7 @@ dpif_operate(struct dpif *dpif, struct dpif_op **ops, size_t n_ops)
                 /* Help the dpif provider to execute one op. */
                 struct dpif_op *op = ops[0];
 
-                op->error = dpif_execute__(dpif, &op->u.execute);
+                op->error = dpif_execute(dpif, &op->u.execute);
                 ops++;
                 n_ops--;
             }
@@ -1277,11 +1228,11 @@ dpif_operate(struct dpif *dpif, struct dpif_op **ops, size_t n_ops)
                 break;
 
             case DPIF_OP_EXECUTE:
-                op->error = dpif_execute__(dpif, &op->u.execute);
+                op->error = dpif_execute(dpif, &op->u.execute);
                 break;
 
             default:
-                NOT_REACHED();
+                OVS_NOT_REACHED();
             }
         }
     }
@@ -1317,10 +1268,15 @@ dpif_recv_set(struct dpif *dpif, bool enable)
  * '*upcall', using 'buf' for storage.  Should only be called if
  * dpif_recv_set() has been used to enable receiving packets on 'dpif'.
  *
- * 'upcall->packet' and 'upcall->key' point into data in the caller-provided
- * 'buf', so their memory cannot be freed separately from 'buf'.  (This is
- * hardly a great way to do things but it works out OK for the dpif providers
- * and clients that exist so far.)
+ * 'upcall->key' and 'upcall->userdata' point into data in the caller-provided
+ * 'buf', so their memory cannot be freed separately from 'buf'.
+ *
+ * The caller owns the data of 'upcall->packet' and may modify it.  If
+ * packet's headroom is exhausted as it is manipulated, 'upcall->packet'
+ * will be reallocated.  This requires the data of 'upcall->packet' to be
+ * released with ofpbuf_uninit() before 'upcall' is destroyed.  However,
+ * when an error is returned, the 'upcall->packet' may be uninitialized
+ * and should not be released.
  *
  * Returns 0 if successful, otherwise a positive errno value.  Returns EAGAIN
  * if no upcall is immediately available. */
@@ -1332,8 +1288,8 @@ dpif_recv(struct dpif *dpif, struct dpif_upcall *upcall, struct ofpbuf *buf)
         struct ds flow;
         char *packet;
 
-        packet = ofp_packet_to_string(upcall->packet->data,
-                                      upcall->packet->size);
+        packet = ofp_packet_to_string(upcall->packet.data,
+                                      upcall->packet.size);
 
         ds_init(&flow);
         odp_flow_key_format(upcall->key, upcall->key_len, &flow);
index 499ee79..7f986f9 100644 (file)
  * The terms written in quotes below are defined in later sections.
  *
  * When a datapath "port" receives a packet, it extracts the headers (the
- * "flow").  If the datapath's "flow table" contains a "flow entry" whose flow
- * is the same as the packet's, then it executes the "actions" in the flow
- * entry and increments the flow's statistics.  If there is no matching flow
- * entry, the datapath instead appends the packet to an "upcall" queue.
+ * "flow").  If the datapath's "flow table" contains a "flow entry" matching
+ * the packet, then it executes the "actions" in the flow entry and increments
+ * the flow's statistics.  If there is no matching flow entry, the datapath
+ * instead appends the packet to an "upcall" queue.
  *
  *
  * Ports
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
-#include "openflow/openflow.h"
 #include "netdev.h"
+#include "ofpbuf.h"
+#include "openflow/openflow.h"
+#include "packets.h"
 #include "util.h"
 
 #ifdef  __cplusplus
@@ -380,7 +382,6 @@ struct dpif;
 struct ds;
 struct flow;
 struct nlattr;
-struct ofpbuf;
 struct sset;
 struct dpif_class;
 
@@ -413,9 +414,9 @@ struct dpif_dp_stats {
     uint64_t n_missed;          /* Number of flow table misses. */
     uint64_t n_lost;            /* Number of misses not sent to userspace. */
     uint64_t n_flows;           /* Number of flows present. */
-    uint64_t n_masks;           /* Number of mega flow masks. */
     uint64_t n_mask_hit;        /* Number of mega flow masks visited for
                                    flow table matches. */
+    uint32_t n_masks;           /* Number of mega flow masks. */
 };
 int dpif_get_dp_stats(const struct dpif *, struct dpif_dp_stats *);
 
@@ -516,14 +517,6 @@ bool dpif_flow_dump_next(struct dpif_flow_dump *,
                          const struct dpif_flow_stats **);
 int dpif_flow_dump_done(struct dpif_flow_dump *);
 \f
-/* Packet operations. */
-
-int dpif_execute(struct dpif *,
-                 const struct nlattr *key, size_t key_len,
-                 const struct nlattr *actions, size_t actions_len,
-                 const struct ofpbuf *,
-                 bool needs_help);
-\f
 /* Operation batching interface.
  *
  * Some datapaths are faster at performing N operations together than the same
@@ -561,11 +554,10 @@ struct dpif_flow_del {
 
 struct dpif_execute {
     /* Raw support for execute passed along to the provider. */
-    const struct nlattr *key;       /* Partial flow key (only for metadata). */
-    size_t key_len;                 /* Length of 'key' in bytes. */
     const struct nlattr *actions;   /* Actions to execute on packet. */
     size_t actions_len;             /* Length of 'actions' in bytes. */
-    const struct ofpbuf *packet;    /* Packet to execute. */
+    struct ofpbuf *packet;          /* Packet to execute. */
+    struct pkt_metadata md;         /* Packet metadata. */
 
     /* Some dpif providers do not implement every action.  The Linux kernel
      * datapath, in particular, does not implement ARP field modification.
@@ -577,6 +569,8 @@ struct dpif_execute {
     bool needs_help;
 };
 
+int dpif_execute(struct dpif *, struct dpif_execute *);
+
 struct dpif_op {
     enum dpif_op_type type;
     int error;
@@ -601,15 +595,17 @@ const char *dpif_upcall_type_to_string(enum dpif_upcall_type);
 
 /* A packet passed up from the datapath to userspace.
  *
- * If 'key', 'actions', or 'userdata' is nonnull, then it points into data
- * owned by 'packet', so their memory cannot be freed separately.  (This is
- * hardly a great way to do things but it works out OK for the dpif providers
- * and clients that exist so far.)
+ * The 'packet', 'key' and 'userdata' may point into data in a buffer
+ * provided by the caller, so the buffer should be released only after the
+ * upcall processing has been finished.
+ *
+ * While being processed, the 'packet' may be reallocated, so the packet must
+ * be separately released with ofpbuf_uninit().
  */
 struct dpif_upcall {
     /* All types. */
     enum dpif_upcall_type type;
-    struct ofpbuf *packet;      /* Packet data. */
+    struct ofpbuf packet;       /* Packet data. */
     struct nlattr *key;         /* Flow key. */
     size_t key_len;             /* Length of 'key' in bytes. */
 
index 02f56e0..fd73566 100644 (file)
@@ -20,6 +20,9 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <unistd.h>
+#ifdef _WIN32
+#include <Wincrypt.h>
+#endif
 
 #include "socket-util.h"
 #include "vlog.h"
@@ -33,6 +36,7 @@ static const char urandom[] = "/dev/urandom";
 int
 get_entropy(void *buffer, size_t n)
 {
+#ifndef _WIN32
     size_t bytes_read;
     int error;
     int fd;
@@ -49,6 +53,31 @@ get_entropy(void *buffer, size_t n)
     if (error) {
         VLOG_ERR("%s: read error (%s)", urandom, ovs_retval_to_string(error));
     }
+#else
+    int error = 0;
+    HCRYPTPROV   crypt_prov = 0;
+    LPVOID msg_buf;
+
+    CryptAcquireContext(&crypt_prov, NULL, NULL,
+                        PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
+    if (!CryptGenRandom(crypt_prov, n, buffer)) {
+        error = EINVAL;
+        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
+                      | FORMAT_MESSAGE_FROM_SYSTEM
+                      | FORMAT_MESSAGE_IGNORE_INSERTS,
+                      NULL,
+                      GetLastError(),
+                      0,
+                      (LPTSTR)&msg_buf,
+                      0,
+                      NULL
+            );
+        VLOG_ERR("CryptGenRandom: read error (%s)", msg_buf);
+        LocalFree(msg_buf);
+    }
+
+    CryptReleaseContext(crypt_prov, 0);
+#endif
     return error;
 }
 
index e980f4b..b1a0341 100644 (file)
@@ -174,7 +174,7 @@ fatal_signal_run(void)
         raise(sig_nr);
 
         ovs_mutex_unlock(&mutex);
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
index 0dfd01f..f1d2cad 100644 (file)
@@ -519,6 +519,18 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
     }
 }
 
+void
+flow_unwildcard_tp_ports(const struct flow *flow, struct flow_wildcards *wc)
+{
+    if (flow->nw_proto != IPPROTO_ICMP) {
+        memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
+        memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
+    } else {
+        wc->masks.tp_src = htons(0xff);
+        wc->masks.tp_dst = htons(0xff);
+    }
+}
+
 /* Initializes 'fmd' with the metadata found in 'flow'. */
 void
 flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
@@ -630,6 +642,15 @@ flow_wildcards_init_catchall(struct flow_wildcards *wc)
     memset(&wc->masks, 0, sizeof wc->masks);
 }
 
+/* Clear the metadata and register wildcard masks. They are not packet
+ * header fields. */
+void
+flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc)
+{
+    memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
+    memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
+}
+
 /* Returns true if 'wc' matches every packet, false if 'wc' fixes any bits or
  * fields. */
 bool
@@ -704,24 +725,22 @@ flow_wildcards_fold_minimask(struct flow_wildcards *wc,
     flow_union_with_miniflow(&wc->masks, &mask->masks);
 }
 
-inline uint64_t
+uint64_t
 miniflow_get_map_in_range(const struct miniflow *miniflow,
-                          uint8_t start, uint8_t end, const uint32_t **data)
+                          uint8_t start, uint8_t end, unsigned int *offset)
 {
     uint64_t map = miniflow->map;
-    uint32_t *p = miniflow->values;
+    *offset = 0;
 
     if (start > 0) {
         uint64_t msk = (UINT64_C(1) << start) - 1; /* 'start' LSBs set */
-        p += count_1bits(map & msk);  /* Skip to start. */
+        *offset = count_1bits(map & msk);
         map &= ~msk;
     }
     if (end < FLOW_U32S) {
         uint64_t msk = (UINT64_C(1) << end) - 1; /* 'end' LSBs set */
         map &= msk;
     }
-
-    *data = p;
     return map;
 }
 
@@ -733,8 +752,10 @@ flow_wildcards_fold_minimask_range(struct flow_wildcards *wc,
                                    uint8_t start, uint8_t end)
 {
     uint32_t *dst_u32 = (uint32_t *)&wc->masks;
-    const uint32_t *p;
-    uint64_t map = miniflow_get_map_in_range(&mask->masks, start, end, &p);
+    unsigned int offset;
+    uint64_t map = miniflow_get_map_in_range(&mask->masks, start, end,
+                                             &offset);
+    const uint32_t *p = mask->masks.values + offset;
 
     for (; map; map = zero_rightmost_1bit(map)) {
         dst_u32[raw_ctz(map)] |= *p++;
@@ -913,14 +934,13 @@ flow_mask_hash_fields(const struct flow *flow, struct flow_wildcards *wc,
         }
         if (is_ip_any(flow)) {
             memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
-            memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
-            memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
+            flow_unwildcard_tp_ports(flow, wc);
         }
         wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -938,7 +958,7 @@ flow_hash_fields(const struct flow *flow, enum nx_hash_fields fields,
         return flow_hash_symmetric_l4(flow, basis);
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 /* Returns a string representation of 'fields'. */
@@ -1241,12 +1261,16 @@ miniflow_alloc_values(struct miniflow *flow, int n)
 
 /* Completes an initialization of 'dst' as a miniflow copy of 'src' begun by
  * the caller.  The caller must have already initialized 'dst->map' properly
- * to indicate the nonzero uint32_t elements of 'src'.  'n' must be the number
- * of 1-bits in 'dst->map'.
+ * to indicate the significant uint32_t elements of 'src'.  'n' must be the
+ * number of 1-bits in 'dst->map'.
+ *
+ * Normally the significant elements are the ones that are non-zero.  However,
+ * when a miniflow is initialized from a (mini)mask, the values can be zeroes,
+ * so that the flow and mask always have the same maps.
  *
  * This function initializes 'dst->values' (either inline if possible or with
- * malloc() otherwise) and copies the nonzero uint32_t elements of 'src' into
- * it. */
+ * malloc() otherwise) and copies the uint32_t elements of 'src' indicated by
+ * 'dst->map' into it. */
 static void
 miniflow_init__(struct miniflow *dst, const struct flow *src, int n)
 {
@@ -1492,11 +1516,7 @@ miniflow_hash_in_minimask(const struct miniflow *flow,
     hash = basis;
 
     for (map = mask->masks.map; map; map = zero_rightmost_1bit(map)) {
-        if (*p) {
-            int ofs = raw_ctz(map);
-            hash = mhash_add(hash, miniflow_get(flow, ofs) & *p);
-        }
-        p++;
+        hash = mhash_add(hash, miniflow_get(flow, raw_ctz(map)) & *p++);
     }
 
     return mhash_finish(hash, (p - mask->masks.values) * 4);
@@ -1518,10 +1538,7 @@ flow_hash_in_minimask(const struct flow *flow, const struct minimask *mask,
 
     hash = basis;
     for (map = mask->masks.map; map; map = zero_rightmost_1bit(map)) {
-        if (*p) {
-            hash = mhash_add(hash, flow_u32[raw_ctz(map)] & *p);
-        }
-        p++;
+        hash = mhash_add(hash, flow_u32[raw_ctz(map)] & *p++);
     }
 
     return mhash_finish(hash, (p - mask->masks.values) * 4);
@@ -1538,15 +1555,14 @@ flow_hash_in_minimask_range(const struct flow *flow,
                             uint8_t start, uint8_t end, uint32_t *basis)
 {
     const uint32_t *flow_u32 = (const uint32_t *)flow;
-    const uint32_t *p;
-    uint64_t map = miniflow_get_map_in_range(&mask->masks, start, end, &p);
+    unsigned int offset;
+    uint64_t map = miniflow_get_map_in_range(&mask->masks, start, end,
+                                             &offset);
+    const uint32_t *p = mask->masks.values + offset;
     uint32_t hash = *basis;
 
     for (; map; map = zero_rightmost_1bit(map)) {
-        if (*p) {
-            hash = mhash_add(hash, flow_u32[raw_ctz(map)] & *p);
-        }
-        p++;
+        hash = mhash_add(hash, flow_u32[raw_ctz(map)] & *p++);
     }
 
     *basis = hash; /* Allow continuation from the unfinished value. */
index 5c0007d..9e8549d 100644 (file)
@@ -174,6 +174,7 @@ void flow_extract(struct ofpbuf *, uint32_t priority, uint32_t mark,
                   struct flow *);
 
 void flow_zero_wildcards(struct flow *, const struct flow_wildcards *);
+void flow_unwildcard_tp_ports(const struct flow *, struct flow_wildcards *);
 void flow_get_metadata(const struct flow *, struct flow_metadata *);
 
 char *flow_to_string(const struct flow *);
@@ -284,6 +285,8 @@ struct flow_wildcards {
 
 void flow_wildcards_init_catchall(struct flow_wildcards *);
 
+void flow_wildcards_clear_non_packet_fields(struct flow_wildcards *);
+
 bool flow_wildcards_is_catchall(const struct flow_wildcards *);
 
 void flow_wildcards_set_reg_mask(struct flow_wildcards *,
@@ -359,7 +362,9 @@ BUILD_ASSERT_DECL(FLOW_U32S <= 64);
  *
  * Elements in 'values' are allowed to be zero.  This is useful for "struct
  * minimatch", for which ensuring that the miniflow and minimask members have
- * same 'map' allows optimization .
+ * same 'map' allows optimization.  This allowance applies only to a miniflow
+ * that is not a mask.  That is, a minimask may NOT have zero elements in
+ * its 'values'.
  */
 struct miniflow {
     uint64_t map;
@@ -390,14 +395,19 @@ bool miniflow_equal_flow_in_minimask(const struct miniflow *a,
 uint32_t miniflow_hash(const struct miniflow *, uint32_t basis);
 uint32_t miniflow_hash_in_minimask(const struct miniflow *,
                                    const struct minimask *, uint32_t basis);
-uint64_t miniflow_get_map_in_range(const struct miniflow *, uint8_t start,
-                                   uint8_t end, const uint32_t **data);
+uint64_t miniflow_get_map_in_range(const struct miniflow *miniflow,
+                                   uint8_t start, uint8_t end,
+                                   unsigned int *offset);
+
 \f
 /* Compressed flow wildcards. */
 
 /* A sparse representation of a "struct flow_wildcards".
  *
- * See the large comment on struct miniflow for details. */
+ * See the large comment on struct miniflow for details.
+ *
+ * Note: While miniflow can have zero data for a 1-bit in the map,
+ * a minimask may not!  We rely on this in the implementation. */
 struct minimask {
     struct miniflow masks;
 };
index c08c368..c59b51b 100644 (file)
@@ -91,7 +91,7 @@ jhash_words(const uint32_t *p, size_t n, uint32_t basis)
 
 /* Returns the Jenkins hash of the 'n' bytes at 'p', starting from 'basis'.
  *
- * Use jhash_bytes() instead, unless you're computing a hash function whose
+ * Use hash_bytes() instead, unless you're computing a hash function whose
  * value is exposed "on the wire" so we don't want to change it. */
 uint32_t
 jhash_bytes(const void *p_, size_t n, uint32_t basis)
index 56dc5ef..db0e09e 100644 (file)
@@ -355,7 +355,7 @@ json_destroy(struct json *json)
             break;
 
         case JSON_N_TYPES:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
         free(json);
     }
@@ -417,7 +417,7 @@ json_clone(const struct json *json)
 
     case JSON_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -502,7 +502,7 @@ json_hash(const struct json *json, size_t basis)
 
     case JSON_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -573,7 +573,7 @@ json_equal(const struct json *a, const struct json *b)
 
     case JSON_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 \f
@@ -1175,7 +1175,7 @@ json_parser_put_value(struct json_parser *p, struct json *value)
     } else if (node->json->type == JSON_ARRAY) {
         json_array_add(node->json, value);
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1295,7 +1295,7 @@ json_parser_pop(struct json_parser *p)
         } else if (node->json->type == JSON_OBJECT) {
             p->parse_state = JSON_PARSE_OBJECT_NEXT;
         } else {
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
 }
@@ -1502,7 +1502,7 @@ json_serialize(const struct json *json, struct json_serializer *s)
 
     case JSON_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1750,6 +1750,6 @@ json_serialized_length(const struct json *json)
 
     case JSON_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
index fce65b3..9eec1f9 100644 (file)
 
 #include <stdlib.h>
 
+#include "connectivity.h"
 #include "dynamic-string.h"
 #include "hash.h"
 #include "hmap.h"
 #include "ofpbuf.h"
 #include "packets.h"
 #include "poll-loop.h"
+#include "seq.h"
 #include "shash.h"
 #include "timer.h"
 #include "timeval.h"
@@ -509,11 +511,16 @@ lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu) OVS_EXCLUDED(mutex)
     ovs_mutex_lock(&mutex);
     HMAP_FOR_EACH (slave, node, &lacp->slaves) {
         if (timer_expired(&slave->rx)) {
+            enum slave_status old_status = slave->status;
+
             if (slave->status == LACP_CURRENT) {
                 slave_set_expired(slave);
             } else if (slave->status == LACP_EXPIRED) {
                 slave_set_defaulted(slave);
             }
+            if (slave->status != old_status) {
+                seq_change(connectivity_seq_get());
+            }
         }
     }
 
@@ -544,6 +551,7 @@ lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu) OVS_EXCLUDED(mutex)
                         : LACP_SLOW_TIME_TX);
 
             timer_set_duration(&slave->tx, duration);
+            seq_change(connectivity_seq_get());
         }
     }
     ovs_mutex_unlock(&mutex);
@@ -897,7 +905,7 @@ lacp_print_details(struct ds *ds, struct lacp *lacp) OVS_REQUIRES(mutex)
             status = "defaulted";
             break;
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
 
         ds_put_format(ds, "\nslave: %s: %s %s\n", slave->name, status,
index fe06744..c9c1ae9 100644 (file)
@@ -290,7 +290,7 @@ mac_learning_lookup(const struct mac_learning *ml,
     } else {
         struct mac_entry *e = mac_entry_lookup(ml, dst, vlan);
 
-        ovs_assert(e == NULL || e->port.p != NULL)
+        ovs_assert(e == NULL || e->port.p != NULL);
         return e;
     }
 }
index 9e5da13..cc18a6a 100644 (file)
@@ -1184,20 +1184,21 @@ uint32_t
 minimatch_hash_range(const struct minimatch *match, uint8_t start, uint8_t end,
                      uint32_t *basis)
 {
-    const uint32_t *p;
-    uint64_t map = miniflow_get_map_in_range(&match->mask.masks, start, end,
-                                             &p);
-    const ptrdiff_t df = match->mask.masks.values - match->flow.values;
+    unsigned int offset;
+    const uint32_t *p, *q;
     uint32_t hash = *basis;
+    int n, i;
 
-    for (; map; map = zero_rightmost_1bit(map)) {
-        if (*p) {
-            hash = mhash_add(hash, *(p - df) & *p);
-        }
-        p++;
+    n = count_1bits(miniflow_get_map_in_range(&match->mask.masks, start, end,
+                                              &offset));
+    q = match->mask.masks.values + offset;
+    p = match->flow.values + offset;
+
+    for (i = 0; i < n; i++) {
+        hash = mhash_add(hash, p[i] & q[i]);
     }
     *basis = hash; /* Allow continuation from the unfinished value. */
-    return mhash_finish(hash, (p - match->mask.masks.values) * 4);
+    return mhash_finish(hash, (offset + n) * 4);
 }
 
 /* Appends a string representation of 'match' to 's'.  If 'priority' is
index bc972b0..96e0efe 100644 (file)
@@ -37,6 +37,9 @@
 
 VLOG_DEFINE_THIS_MODULE(meta_flow);
 
+#define FLOW_U32OFS(FIELD)                                              \
+    offsetof(struct flow, FIELD) % 4 ? -1 : offsetof(struct flow, FIELD) / 4
+
 #define MF_FIELD_SIZES(MEMBER)                  \
     sizeof ((union mf_value *)0)->MEMBER,       \
     8 * sizeof ((union mf_value *)0)->MEMBER
@@ -59,6 +62,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_TUNNEL_ID, "OXM_OF_TUNNEL_ID",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        FLOW_U32OFS(tunnel.tun_id),
     }, {
         MFF_TUN_SRC, "tun_src", NULL,
         MF_FIELD_SIZES(be32),
@@ -70,6 +74,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         NXM_NX_TUN_IPV4_SRC, "NXM_NX_TUN_IPV4_SRC",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        FLOW_U32OFS(tunnel.ip_src),
     }, {
         MFF_TUN_DST, "tun_dst", NULL,
         MF_FIELD_SIZES(be32),
@@ -81,6 +86,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         NXM_NX_TUN_IPV4_DST, "NXM_NX_TUN_IPV4_DST",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        FLOW_U32OFS(tunnel.ip_dst),
     }, {
         MFF_TUN_FLAGS, "tun_flags", NULL,
         MF_FIELD_SIZES(be16),
@@ -92,6 +98,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         0, NULL,
         OFPUTIL_P_NONE,
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_TUN_TTL, "tun_ttl", NULL,
         MF_FIELD_SIZES(u8),
@@ -103,6 +110,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         0, NULL,
         OFPUTIL_P_NONE,
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_TUN_TOS, "tun_tos", NULL,
         MF_FIELD_SIZES(u8),
@@ -114,6 +122,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         0, NULL,
         OFPUTIL_P_NONE,
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_METADATA, "metadata", NULL,
         MF_FIELD_SIZES(be64),
@@ -125,6 +134,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_METADATA, "OXM_OF_METADATA",
         OFPUTIL_P_NXM_OF11_UP,
         OFPUTIL_P_NXM_OF11_UP,
+        -1,
     }, {
         MFF_IN_PORT, "in_port", NULL,
         MF_FIELD_SIZES(be16),
@@ -136,6 +146,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         NXM_OF_IN_PORT, "NXM_OF_IN_PORT",
         OFPUTIL_P_ANY,   /* OF11+ via mapping to 32 bits. */
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_IN_PORT_OXM, "in_port_oxm", NULL,
         MF_FIELD_SIZES(be32),
@@ -147,6 +158,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_IN_PORT, "OXM_OF_IN_PORT",
         OFPUTIL_P_OF11_UP,
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_SKB_PRIORITY, "skb_priority", NULL,
         MF_FIELD_SIZES(be32),
@@ -158,6 +170,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         0, NULL,
         OFPUTIL_P_NONE,
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_PKT_MARK, "pkt_mark", NULL,
         MF_FIELD_SIZES(be32),
@@ -169,6 +182,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         NXM_NX_PKT_MARK, "NXM_NX_PKT_MARK",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     },
 
 #define REGISTER(IDX)                           \
@@ -183,6 +197,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         NXM_NX_REG(IDX), "NXM_NX_REG" #IDX,     \
         OFPUTIL_P_NXM_OXM_ANY,                  \
         OFPUTIL_P_NXM_OXM_ANY,                  \
+        -1,                                     \
     }
 #if FLOW_N_REGS > 0
     REGISTER(0),
@@ -227,6 +242,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_ETH_SRC, "OXM_OF_ETH_SRC",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NXM_OF11_UP,   /* Bitwise masking only with NXM and OF11+! */
+        -1,
     }, {
         MFF_ETH_DST, "eth_dst", "dl_dst",
         MF_FIELD_SIZES(mac),
@@ -238,6 +254,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_ETH_DST, "OXM_OF_ETH_DST",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NXM_OF11_UP,   /* Bitwise masking only with NXM and OF11+! */
+        -1,
     }, {
         MFF_ETH_TYPE, "eth_type", "dl_type",
         MF_FIELD_SIZES(be16),
@@ -249,6 +266,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_ETH_TYPE, "OXM_OF_ETH_TYPE",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NONE,
+        -1,
     },
 
     {
@@ -262,6 +280,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         NXM_OF_VLAN_TCI, "NXM_OF_VLAN_TCI",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     }, {
         MFF_DL_VLAN, "dl_vlan", NULL,
         sizeof(ovs_be16), 12,
@@ -273,6 +292,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         0, NULL,
         OFPUTIL_P_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     }, {
         MFF_VLAN_VID, "vlan_vid", NULL,
         sizeof(ovs_be16), 12,
@@ -284,6 +304,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_VLAN_VID, "OXM_OF_VLAN_VID",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     }, {
         MFF_DL_VLAN_PCP, "dl_vlan_pcp", NULL,
         1, 3,
@@ -295,6 +316,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         0, NULL,
         OFPUTIL_P_ANY,   /* Will be mapped to NXM and OXM. */
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_VLAN_PCP, "vlan_pcp", NULL,
         1, 3,
@@ -306,6 +328,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_VLAN_PCP, "OXM_OF_VLAN_PCP",
         OFPUTIL_P_ANY,   /* Will be mapped to OF10 and NXM. */
         OFPUTIL_P_NONE,
+        -1,
     },
 
     /* ## ---- ## */
@@ -322,6 +345,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_MPLS_LABEL, "OXM_OF_MPLS_LABEL",
         OFPUTIL_P_NXM_OF11_UP,
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_MPLS_TC, "mpls_tc", NULL,
         1, 3,
@@ -333,6 +357,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_MPLS_TC, "OXM_OF_MPLS_TC",
         OFPUTIL_P_NXM_OF11_UP,
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_MPLS_BOS, "mpls_bos", NULL,
         1, 1,
@@ -344,6 +369,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_MPLS_BOS, "OXM_OF_MPLS_BOS",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NONE,
+        -1,
     },
 
     /* ## -- ## */
@@ -361,6 +387,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_IPV4_SRC, "OXM_OF_IPV4_SRC",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NXM_OF11_UP,
+        FLOW_U32OFS(nw_src),
     }, {
         MFF_IPV4_DST, "ip_dst", "nw_dst",
         MF_FIELD_SIZES(be32),
@@ -372,6 +399,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_IPV4_DST, "OXM_OF_IPV4_DST",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NXM_OF11_UP,
+        FLOW_U32OFS(nw_dst),
     },
 
     {
@@ -385,6 +413,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_IPV6_SRC, "OXM_OF_IPV6_SRC",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        FLOW_U32OFS(ipv6_src),
     }, {
         MFF_IPV6_DST, "ipv6_dst", NULL,
         MF_FIELD_SIZES(ipv6),
@@ -396,6 +425,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_IPV6_DST, "OXM_OF_IPV6_DST",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        FLOW_U32OFS(ipv6_dst),
     },
     {
         MFF_IPV6_LABEL, "ipv6_label", NULL,
@@ -408,6 +438,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_IPV6_FLABEL, "OXM_OF_IPV6_FLABEL",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     },
 
     {
@@ -421,6 +452,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_IP_PROTO, "OXM_OF_IP_PROTO",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_IP_DSCP, "nw_tos", NULL,
         MF_FIELD_SIZES(u8),
@@ -432,6 +464,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         NXM_OF_IP_TOS, "NXM_OF_IP_TOS",
         OFPUTIL_P_ANY,   /* Will be shifted for OXM. */
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_IP_DSCP_SHIFTED, "ip_dscp", NULL,
         1, 6,
@@ -443,6 +476,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_IP_DSCP, "OXM_OF_IP_DSCP",
         OFPUTIL_P_ANY,   /* Will be shifted for non-OXM. */
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_IP_ECN, "nw_ecn", "ip_ecn",
         1, 2,
@@ -454,6 +488,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_IP_ECN, "OXM_OF_IP_ECN",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_IP_TTL, "nw_ttl", NULL,
         MF_FIELD_SIZES(u8),
@@ -465,6 +500,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         NXM_NX_IP_TTL, "NXM_NX_IP_TTL",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_IP_FRAG, "ip_frag", NULL,
         1, 2,
@@ -476,6 +512,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         NXM_NX_IP_FRAG, "NXM_NX_IP_FRAG",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     },
 
     {
@@ -489,6 +526,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_ARP_OP, "OXM_OF_ARP_OP",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_ARP_SPA, "arp_spa", NULL,
         MF_FIELD_SIZES(be32),
@@ -500,6 +538,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_ARP_SPA, "OXM_OF_ARP_SPA",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NXM_OF11_UP,
+        -1,
     }, {
         MFF_ARP_TPA, "arp_tpa", NULL,
         MF_FIELD_SIZES(be32),
@@ -511,6 +550,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_ARP_TPA, "OXM_OF_ARP_TPA",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NXM_OF11_UP,
+        -1,
     }, {
         MFF_ARP_SHA, "arp_sha", NULL,
         MF_FIELD_SIZES(mac),
@@ -522,6 +562,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_ARP_SHA, "OXM_OF_ARP_SHA",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     }, {
         MFF_ARP_THA, "arp_tha", NULL,
         MF_FIELD_SIZES(mac),
@@ -533,6 +574,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_ARP_THA, "OXM_OF_ARP_THA",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     },
 
     /* ## -- ## */
@@ -550,6 +592,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_TCP_SRC, "OXM_OF_TCP_SRC",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     }, {
         MFF_TCP_DST, "tcp_dst", "tp_dst",
         MF_FIELD_SIZES(be16),
@@ -561,6 +604,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_TCP_DST, "OXM_OF_TCP_DST",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     }, {
         MFF_TCP_FLAGS, "tcp_flags", NULL,
         2, 12,
@@ -572,6 +616,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         NXM_NX_TCP_FLAGS, "NXM_NX_TCP_FLAGS",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     },
 
     {
@@ -585,6 +630,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_UDP_SRC, "OXM_OF_UDP_SRC",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     }, {
         MFF_UDP_DST, "udp_dst", NULL,
         MF_FIELD_SIZES(be16),
@@ -596,6 +642,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_UDP_DST, "OXM_OF_UDP_DST",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     },
 
     {
@@ -609,6 +656,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_SCTP_SRC, "OXM_OF_SCTP_SRC",
         OFPUTIL_P_NXM_OF11_UP,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     }, {
         MFF_SCTP_DST, "sctp_dst", NULL,
         MF_FIELD_SIZES(be16),
@@ -620,6 +668,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_SCTP_DST, "OXM_OF_SCTP_DST",
         OFPUTIL_P_NXM_OF11_UP,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     },
 
     {
@@ -633,6 +682,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_ICMPV4_TYPE, "OXM_OF_ICMPV4_TYPE",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_ICMPV4_CODE, "icmp_code", NULL,
         MF_FIELD_SIZES(u8),
@@ -644,6 +694,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_ICMPV4_CODE, "OXM_OF_ICMPV4_CODE",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NONE,
+        -1,
     },
 
     {
@@ -657,6 +708,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_ICMPV6_TYPE, "OXM_OF_ICMPV6_TYPE",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NONE,
+        -1,
     }, {
         MFF_ICMPV6_CODE, "icmpv6_code", NULL,
         MF_FIELD_SIZES(u8),
@@ -668,6 +720,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_ICMPV6_CODE, "OXM_OF_ICMPV6_CODE",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NONE,
+        -1,
     },
 
     /* ## ---- ## */
@@ -685,6 +738,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_IPV6_ND_TARGET, "OXM_OF_IPV6_ND_TARGET",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     }, {
         MFF_ND_SLL, "nd_sll", NULL,
         MF_FIELD_SIZES(mac),
@@ -696,6 +750,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_IPV6_ND_SLL, "OXM_OF_IPV6_ND_SLL",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     }, {
         MFF_ND_TLL, "nd_tll", NULL,
         MF_FIELD_SIZES(mac),
@@ -707,6 +762,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_IPV6_ND_TLL, "OXM_OF_IPV6_ND_TLL",
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+        -1,
     }
 };
 
@@ -929,7 +985,7 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
 
     case MFF_N_IDS:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -960,7 +1016,7 @@ mf_is_mask_valid(const struct mf_field *mf, const union mf_value *mask)
         return true;
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 static bool
@@ -1025,7 +1081,7 @@ mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow)
                 && (flow->tp_src == htons(ND_NEIGHBOR_ADVERT)));
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 /* Set field and it's prerequisities in the mask.
@@ -1164,7 +1220,7 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
 
     case MFF_N_IDS:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1355,7 +1411,7 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
 
     case MFF_N_IDS:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1551,7 +1607,7 @@ mf_set_value(const struct mf_field *mf,
 
     case MFF_N_IDS:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1767,7 +1823,7 @@ mf_set_flow_value(const struct mf_field *mf,
 
     case MFF_N_IDS:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1975,7 +2031,7 @@ mf_set_wild(const struct mf_field *mf, struct match *match)
 
     case MFF_N_IDS:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -2143,7 +2199,7 @@ mf_set(const struct mf_field *mf,
 
     case MFF_N_IDS:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return mf->usable_protocols_bitwise;
@@ -2617,7 +2673,7 @@ mf_parse(const struct mf_field *mf, const char *s,
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     if (!error && !mf_is_mask_valid(mf, mask)) {
@@ -2771,7 +2827,7 @@ mf_format(const struct mf_field *mf,
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 \f
index 2c5616f..cf92556 100644 (file)
@@ -297,6 +297,9 @@ struct mf_field {
     enum ofputil_protocol usable_protocols; /* If fully/cidr masked. */
     /* If partially/non-cidr masked. */
     enum ofputil_protocol usable_protocols_bitwise;
+
+    int flow_be32ofs;  /* Field's be32 offset in "struct flow", if prefix tree
+                        * lookup is supported for the field, or -1. */
 };
 
 /* The representation of a field's value. */
index d611008..a6f549c 100644 (file)
@@ -189,7 +189,7 @@ multipath_algorithm(uint32_t hash, enum nx_mp_algorithm algorithm,
         return algorithm_iter_hash(hash, n_links, arg);
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 \f
 /* Parses 's_' as a set of arguments to the "multipath" action and initializes
index dd27d2d..4a16e5c 100644 (file)
@@ -47,6 +47,7 @@
 #endif
 
 #include "rtbsd.h"
+#include "connectivity.h"
 #include "coverage.h"
 #include "dynamic-string.h"
 #include "fatal-signal.h"
@@ -55,8 +56,9 @@
 #include "ovs-thread.h"
 #include "packets.h"
 #include "poll-loop.h"
-#include "socket-util.h"
+#include "seq.h"
 #include "shash.h"
+#include "socket-util.h"
 #include "svec.h"
 #include "util.h"
 #include "vlog.h"
@@ -86,7 +88,6 @@ struct netdev_bsd {
     struct ovs_mutex mutex;
 
     unsigned int cache_valid;
-    unsigned int change_seq;
 
     int ifindex;
     uint8_t etheraddr[ETH_ADDR_LEN];
@@ -197,15 +198,6 @@ netdev_bsd_wait(void)
     rtbsd_notifier_wait();
 }
 
-static void
-netdev_bsd_changed(struct netdev_bsd *dev)
-{
-    dev->change_seq++;
-    if (!dev->change_seq) {
-        dev->change_seq++;
-    }
-}
-
 /* Invalidate cache in case of interface status change. */
 static void
 netdev_bsd_cache_cb(const struct rtbsd_change *change,
@@ -223,7 +215,7 @@ netdev_bsd_cache_cb(const struct rtbsd_change *change,
             if (is_netdev_bsd_class(netdev_class)) {
                 dev = netdev_bsd_cast(base_dev);
                 dev->cache_valid = 0;
-                netdev_bsd_changed(dev);
+                seq_change(connectivity_seq_get());
             }
             netdev_close(base_dev);
         }
@@ -241,7 +233,7 @@ netdev_bsd_cache_cb(const struct rtbsd_change *change,
             struct netdev *netdev = node->data;
             dev = netdev_bsd_cast(netdev);
             dev->cache_valid = 0;
-            netdev_bsd_changed(dev);
+            seq_change(connectivity_seq_get());
             netdev_close(netdev);
         }
         shash_destroy(&device_shash);
@@ -294,7 +286,6 @@ netdev_bsd_construct_system(struct netdev *netdev_)
     }
 
     ovs_mutex_init(&netdev->mutex);
-    netdev->change_seq = 1;
     netdev->tap_fd = -1;
     netdev->kernel_name = xstrdup(netdev_->name);
 
@@ -329,7 +320,6 @@ netdev_bsd_construct_tap(struct netdev *netdev_)
      * to retrieve the name of the tap device. */
     ovs_mutex_init(&netdev->mutex);
     netdev->tap_fd = open("/dev/tap", O_RDWR);
-    netdev->change_seq = 1;
     if (netdev->tap_fd < 0) {
         error = errno;
         VLOG_WARN("opening \"/dev/tap\" failed: %s", ovs_strerror(error));
@@ -506,9 +496,6 @@ netdev_bsd_rx_construct(struct netdev_rx *rx_)
         ovs_mutex_lock(&netdev->mutex);
         error = netdev_bsd_open_pcap(netdev_get_kernel_name(netdev_),
                                      &rx->pcap_handle, &rx->fd);
-        if (!error) {
-            netdev_bsd_changed(netdev);
-        }
         ovs_mutex_unlock(&netdev->mutex);
     }
 
@@ -756,7 +743,7 @@ netdev_bsd_set_etheraddr(struct netdev *netdev_,
         if (!error) {
             netdev->cache_valid |= VALID_ETHERADDR;
             memcpy(netdev->etheraddr, mac, ETH_ADDR_LEN);
-            netdev_bsd_changed(netdev);
+            seq_change(connectivity_seq_get());
         }
     }
     ovs_mutex_unlock(&netdev->mutex);
@@ -1165,7 +1152,7 @@ netdev_bsd_set_in4(struct netdev *netdev_, struct in_addr addr,
                 netdev->netmask = mask;
             }
         }
-        netdev_bsd_changed(netdev);
+        seq_change(connectivity_seq_get());
     }
     ovs_mutex_unlock(&netdev->mutex);
 
@@ -1454,7 +1441,6 @@ static int
 netdev_bsd_update_flags(struct netdev *netdev_, enum netdev_flags off,
                         enum netdev_flags on, enum netdev_flags *old_flagsp)
 {
-    struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
     int old_flags, new_flags;
     int error;
 
@@ -1464,18 +1450,12 @@ netdev_bsd_update_flags(struct netdev *netdev_, enum netdev_flags off,
         new_flags = (old_flags & ~nd_to_iff_flags(off)) | nd_to_iff_flags(on);
         if (new_flags != old_flags) {
             error = set_flags(netdev_get_kernel_name(netdev_), new_flags);
-            netdev_bsd_changed(netdev);
+            seq_change(connectivity_seq_get());
         }
     }
     return error;
 }
 
-static unsigned int
-netdev_bsd_change_seq(const struct netdev *netdev)
-{
-    return netdev_bsd_cast(netdev)->change_seq;
-}
-
 
 const struct netdev_class netdev_bsd_class = {
     "system",
@@ -1531,8 +1511,6 @@ const struct netdev_class netdev_bsd_class = {
 
     netdev_bsd_update_flags,
 
-    netdev_bsd_change_seq,
-
     netdev_bsd_rx_alloc,
     netdev_bsd_rx_construct,
     netdev_bsd_rx_destruct,
@@ -1596,8 +1574,6 @@ const struct netdev_class netdev_tap_class = {
 
     netdev_bsd_update_flags,
 
-    netdev_bsd_change_seq,
-
     netdev_bsd_rx_alloc,
     netdev_bsd_rx_construct,
     netdev_bsd_rx_destruct,
index fd30454..9515021 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <errno.h>
 
+#include "connectivity.h"
 #include "flow.h"
 #include "list.h"
 #include "netdev-provider.h"
@@ -30,6 +31,7 @@
 #include "packets.h"
 #include "pcap-file.h"
 #include "poll-loop.h"
+#include "seq.h"
 #include "shash.h"
 #include "sset.h"
 #include "stream.h"
@@ -66,7 +68,6 @@ struct netdev_dummy {
     int mtu OVS_GUARDED;
     struct netdev_stats stats OVS_GUARDED;
     enum netdev_flags flags OVS_GUARDED;
-    unsigned int change_seq OVS_GUARDED;
     int ifindex OVS_GUARDED;
 
     struct pstream *pstream OVS_GUARDED;
@@ -91,8 +92,6 @@ struct netdev_rx_dummy {
 
 static unixctl_cb_func netdev_dummy_set_admin_state;
 static int netdev_dummy_construct(struct netdev *);
-static void netdev_dummy_changed(struct netdev_dummy *netdev)
-    OVS_REQUIRES(netdev->mutex);
 static void netdev_dummy_queue_packet(struct netdev_dummy *, struct ofpbuf *);
 
 static void dummy_stream_close(struct dummy_stream *);
@@ -285,7 +284,6 @@ netdev_dummy_construct(struct netdev *netdev_)
     netdev->hwaddr[5] = n;
     netdev->mtu = 1500;
     netdev->flags = 0;
-    netdev->change_seq = 1;
     netdev->ifindex = -EOPNOTSUPP;
 
     netdev->pstream = NULL;
@@ -571,7 +569,7 @@ netdev_dummy_set_etheraddr(struct netdev *netdev,
     ovs_mutex_lock(&dev->mutex);
     if (!eth_addr_equals(dev->hwaddr, mac)) {
         memcpy(dev->hwaddr, mac, ETH_ADDR_LEN);
-        netdev_dummy_changed(dev);
+        seq_change(connectivity_seq_get());
     }
     ovs_mutex_unlock(&dev->mutex);
 
@@ -666,7 +664,7 @@ netdev_dummy_update_flags__(struct netdev_dummy *netdev,
     netdev->flags |= on;
     netdev->flags &= ~off;
     if (*old_flagsp != netdev->flags) {
-        netdev_dummy_changed(netdev);
+        seq_change(connectivity_seq_get());
     }
 
     return 0;
@@ -686,31 +684,9 @@ netdev_dummy_update_flags(struct netdev *netdev_,
 
     return error;
 }
-
-static unsigned int
-netdev_dummy_change_seq(const struct netdev *netdev_)
-{
-    struct netdev_dummy *netdev = netdev_dummy_cast(netdev_);
-    unsigned int change_seq;
-
-    ovs_mutex_lock(&netdev->mutex);
-    change_seq = netdev->change_seq;
-    ovs_mutex_unlock(&netdev->mutex);
-
-    return change_seq;
-}
 \f
 /* Helper functions. */
 
-static void
-netdev_dummy_changed(struct netdev_dummy *dev)
-{
-    dev->change_seq++;
-    if (!dev->change_seq) {
-        dev->change_seq++;
-    }
-}
-
 static const struct netdev_class dummy_class = {
     "dummy",
     NULL,                       /* init */
@@ -766,8 +742,6 @@ static const struct netdev_class dummy_class = {
 
     netdev_dummy_update_flags,
 
-    netdev_dummy_change_seq,
-
     netdev_dummy_rx_alloc,
     netdev_dummy_rx_construct,
     netdev_dummy_rx_destruct,
index 3e0da48..68d476f 100644 (file)
@@ -48,6 +48,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "connectivity.h"
 #include "coverage.h"
 #include "dpif-linux.h"
 #include "dynamic-string.h"
@@ -65,6 +66,7 @@
 #include "packets.h"
 #include "poll-loop.h"
 #include "rtnetlink-link.h"
+#include "seq.h"
 #include "shash.h"
 #include "socket-util.h"
 #include "sset.h"
@@ -357,7 +359,6 @@ struct netdev_linux {
     struct ovs_mutex mutex;
 
     unsigned int cache_valid;
-    unsigned int change_seq;
 
     bool miimon;                    /* Link status of last poll. */
     long long int miimon_interval;  /* Miimon Poll rate. Disabled if <= 0. */
@@ -426,8 +427,7 @@ static int do_set_addr(struct netdev *netdev,
                        struct in_addr addr);
 static int get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN]);
 static int set_etheraddr(const char *netdev_name, const uint8_t[ETH_ADDR_LEN]);
-static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats);
-static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats);
+static int get_stats_via_netlink(const struct netdev *, struct netdev_stats *);
 static int af_packet_sock(void);
 static bool netdev_linux_miimon_enabled(void);
 static void netdev_linux_miimon_run(void);
@@ -585,10 +585,7 @@ netdev_linux_changed(struct netdev_linux *dev,
                      unsigned int ifi_flags, unsigned int mask)
     OVS_REQUIRES(dev->mutex)
 {
-    dev->change_seq++;
-    if (!dev->change_seq) {
-        dev->change_seq++;
-    }
+    seq_change(connectivity_seq_get());
 
     if ((dev->ifi_flags ^ ifi_flags) & IFF_RUNNING) {
         dev->carrier_resets++;
@@ -640,7 +637,6 @@ static void
 netdev_linux_common_construct(struct netdev_linux *netdev)
 {
     ovs_mutex_init(&netdev->mutex);
-    netdev->change_seq = 1;
 }
 
 /* Creates system and internal devices. */
@@ -1315,34 +1311,6 @@ netdev_linux_miimon_wait(void)
     shash_destroy(&device_shash);
 }
 
-/* Check whether we can we use RTM_GETLINK to get network device statistics.
- * In pre-2.6.19 kernels, this was only available if wireless extensions were
- * enabled. */
-static bool
-check_for_working_netlink_stats(void)
-{
-    /* Decide on the netdev_get_stats() implementation to use.  Netlink is
-     * preferable, so if that works, we'll use it. */
-    int ifindex = do_get_ifindex("lo");
-    if (ifindex < 0) {
-        VLOG_WARN("failed to get ifindex for lo, "
-                  "obtaining netdev stats from proc");
-        return false;
-    } else {
-        struct netdev_stats stats;
-        int error = get_stats_via_netlink(ifindex, &stats);
-        if (!error) {
-            VLOG_DBG("obtaining netdev stats via rtnetlink");
-            return true;
-        } else {
-            VLOG_INFO("RTM_GETLINK failed (%s), obtaining netdev stats "
-                      "via proc (you are probably running a pre-2.6.19 "
-                      "kernel)", ovs_strerror(error));
-            return false;
-        }
-    }
-}
-
 static void
 swap_uint64(uint64_t *a, uint64_t *b)
 {
@@ -1424,38 +1392,6 @@ get_stats_via_vport(const struct netdev *netdev_,
     }
 }
 
-static int
-netdev_linux_sys_get_stats(const struct netdev *netdev_,
-                           struct netdev_stats *stats)
-{
-    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
-    static int use_netlink_stats;
-    int error;
-
-    if (ovsthread_once_start(&once)) {
-        use_netlink_stats = check_for_working_netlink_stats();
-        ovsthread_once_done(&once);
-    }
-
-    if (use_netlink_stats) {
-        int ifindex;
-
-        error = get_ifindex(netdev_, &ifindex);
-        if (!error) {
-            error = get_stats_via_netlink(ifindex, stats);
-        }
-    } else {
-        error = get_stats_via_proc(netdev_get_name(netdev_), stats);
-    }
-
-    if (error) {
-        VLOG_WARN_RL(&rl, "%s: linux-sys get stats failed %d",
-                      netdev_get_name(netdev_), error);
-    }
-    return error;
-
-}
-
 /* Retrieves current device stats for 'netdev-linux'. */
 static int
 netdev_linux_get_stats(const struct netdev *netdev_,
@@ -1467,7 +1403,7 @@ netdev_linux_get_stats(const struct netdev *netdev_,
 
     ovs_mutex_lock(&netdev->mutex);
     get_stats_via_vport(netdev_, stats);
-    error = netdev_linux_sys_get_stats(netdev_, &dev_stats);
+    error = get_stats_via_netlink(netdev_, &dev_stats);
     if (error) {
         if (!netdev->vport_stats_error) {
             error = 0;
@@ -1510,7 +1446,7 @@ netdev_tap_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
 
     ovs_mutex_lock(&netdev->mutex);
     get_stats_via_vport(netdev_, stats);
-    error = netdev_linux_sys_get_stats(netdev_, &dev_stats);
+    error = get_stats_via_netlink(netdev_, &dev_stats);
     if (error) {
         if (!netdev->vport_stats_error) {
             error = 0;
@@ -2597,19 +2533,6 @@ netdev_linux_update_flags(struct netdev *netdev_, enum netdev_flags off,
     return error;
 }
 
-static unsigned int
-netdev_linux_change_seq(const struct netdev *netdev_)
-{
-    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
-    unsigned int change_seq;
-
-    ovs_mutex_lock(&netdev->mutex);
-    change_seq = netdev->change_seq;
-    ovs_mutex_unlock(&netdev->mutex);
-
-    return change_seq;
-}
-
 #define NETDEV_LINUX_CLASS(NAME, CONSTRUCT, GET_STATS, SET_STATS,  \
                            GET_FEATURES, GET_STATUS)            \
 {                                                               \
@@ -2668,8 +2591,6 @@ netdev_linux_change_seq(const struct netdev *netdev_)
                                                                 \
     netdev_linux_update_flags,                                  \
                                                                 \
-    netdev_linux_change_seq,                                    \
-                                                                \
     netdev_linux_rx_alloc,                                      \
     netdev_linux_rx_construct,                                  \
     netdev_linux_rx_destruct,                                   \
@@ -4491,110 +4412,41 @@ netdev_stats_from_rtnl_link_stats(struct netdev_stats *dst,
 }
 
 static int
-get_stats_via_netlink(int ifindex, struct netdev_stats *stats)
+get_stats_via_netlink(const struct netdev *netdev_, struct netdev_stats *stats)
 {
-    /* Policy for RTNLGRP_LINK messages.
-     *
-     * There are *many* more fields in these messages, but currently we only
-     * care about these fields. */
-    static const struct nl_policy rtnlgrp_link_policy[] = {
-        [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false },
-        [IFLA_STATS] = { .type = NL_A_UNSPEC, .optional = true,
-                         .min_len = sizeof(struct rtnl_link_stats) },
-    };
-
     struct ofpbuf request;
     struct ofpbuf *reply;
-    struct ifinfomsg *ifi;
-    struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)];
     int error;
 
     ofpbuf_init(&request, 0);
-    nl_msg_put_nlmsghdr(&request, sizeof *ifi, RTM_GETLINK, NLM_F_REQUEST);
-    ifi = ofpbuf_put_zeros(&request, sizeof *ifi);
-    ifi->ifi_family = PF_UNSPEC;
-    ifi->ifi_index = ifindex;
+    nl_msg_put_nlmsghdr(&request,
+                        sizeof(struct ifinfomsg) + NL_ATTR_SIZE(IFNAMSIZ),
+                        RTM_GETLINK, NLM_F_REQUEST);
+    ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg));
+    nl_msg_put_string(&request, IFLA_IFNAME, netdev_get_name(netdev_));
     error = nl_transact(NETLINK_ROUTE, &request, &reply);
     ofpbuf_uninit(&request);
     if (error) {
         return error;
     }
 
-    if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
-                         rtnlgrp_link_policy,
-                         attrs, ARRAY_SIZE(rtnlgrp_link_policy))) {
-        ofpbuf_delete(reply);
-        return EPROTO;
-    }
-
-    if (!attrs[IFLA_STATS]) {
-        VLOG_WARN_RL(&rl, "RTM_GETLINK reply lacks stats");
-        ofpbuf_delete(reply);
-        return EPROTO;
+    if (ofpbuf_try_pull(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg))) {
+        const struct nlattr *a = nl_attr_find(reply, 0, IFLA_STATS);
+        if (a && nl_attr_get_size(a) >= sizeof(struct rtnl_link_stats)) {
+            netdev_stats_from_rtnl_link_stats(stats, nl_attr_get(a));
+            error = 0;
+        } else {
+            VLOG_WARN_RL(&rl, "RTM_GETLINK reply lacks stats");
+            error = EPROTO;
+        }
+    } else {
+        VLOG_WARN_RL(&rl, "short RTM_GETLINK reply");
+        error = EPROTO;
     }
 
-    netdev_stats_from_rtnl_link_stats(stats, nl_attr_get(attrs[IFLA_STATS]));
 
     ofpbuf_delete(reply);
-
-    return 0;
-}
-
-static int
-get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats)
-{
-    static const char fn[] = "/proc/net/dev";
-    char line[1024];
-    FILE *stream;
-    int ln;
-
-    stream = fopen(fn, "r");
-    if (!stream) {
-        VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, ovs_strerror(errno));
-        return errno;
-    }
-
-    ln = 0;
-    while (fgets(line, sizeof line, stream)) {
-        if (++ln >= 3) {
-            char devname[16];
-#define X64 "%"SCNu64
-            if (!ovs_scan(line,
-                          " %15[^:]:"
-                          X64 X64 X64 X64 X64 X64 X64 "%*u"
-                          X64 X64 X64 X64 X64 X64 X64 "%*u",
-                          devname,
-                          &stats->rx_bytes,
-                          &stats->rx_packets,
-                          &stats->rx_errors,
-                          &stats->rx_dropped,
-                          &stats->rx_fifo_errors,
-                          &stats->rx_frame_errors,
-                          &stats->multicast,
-                          &stats->tx_bytes,
-                          &stats->tx_packets,
-                          &stats->tx_errors,
-                          &stats->tx_dropped,
-                          &stats->tx_fifo_errors,
-                          &stats->collisions,
-                          &stats->tx_carrier_errors)) {
-                VLOG_WARN_RL(&rl, "%s:%d: parse error", fn, ln);
-            } else if (!strcmp(devname, netdev_name)) {
-                stats->rx_length_errors = UINT64_MAX;
-                stats->rx_over_errors = UINT64_MAX;
-                stats->rx_crc_errors = UINT64_MAX;
-                stats->rx_missed_errors = UINT64_MAX;
-                stats->tx_aborted_errors = UINT64_MAX;
-                stats->tx_heartbeat_errors = UINT64_MAX;
-                stats->tx_window_errors = UINT64_MAX;
-                fclose(stream);
-                return 0;
-            }
-        }
-    }
-    VLOG_WARN_RL(&rl, "%s: no stats for %s", fn, netdev_name);
-    fclose(stream);
-    return ENODEV;
+    return error;
 }
 
 static int
index 2442b77..f1b8023 100644 (file)
@@ -148,7 +148,18 @@ struct netdev *netdev_rx_get_netdev(const struct netdev_rx *);
  * Each "dealloc" function frees raw memory that was allocated by the the
  * "alloc" function.  The memory's base and derived members might not have ever
  * been initialized (but if "construct" returned successfully, then it has been
- * "destruct"ed already).  The "dealloc" function is not allowed to fail. */
+ * "destruct"ed already).  The "dealloc" function is not allowed to fail.
+ *
+ *
+ * Device Change Notification
+ * ==========================
+ *
+ * Minimally, implementations are required to report changes to netdev flags,
+ * features, ethernet address or carrier through connectivity_seq. Changes to
+ * other properties are allowed to cause notification through this interface,
+ * although implementations should try to avoid this. connectivity_seq_get()
+ * can be used to acquire a reference to the struct seq. The interface is
+ * described in detail in seq.h. */
 struct netdev_class {
     /* Type of netdevs in this class, e.g. "system", "tap", "gre", etc.
      *
@@ -609,17 +620,6 @@ struct netdev_class {
     int (*update_flags)(struct netdev *netdev, enum netdev_flags off,
                         enum netdev_flags on, enum netdev_flags *old_flags);
 
-    /* Returns a sequence number which indicates changes in one of 'netdev''s
-     * properties.  The returned sequence number must be nonzero so that
-     * callers have a value which they may use as a reset when tracking
-     * 'netdev'.
-     *
-     * Minimally, the returned sequence number is required to change whenever
-     * 'netdev''s flags, features, ethernet address, or carrier changes.  The
-     * returned sequence number is allowed to change even when 'netdev' doesn't
-     * change, although implementations should try to avoid this. */
-    unsigned int (*change_seq)(const struct netdev *netdev);
-
 /* ## ------------------- ## */
 /* ## netdev_rx Functions ## */
 /* ## ------------------- ## */
index 07b2381..165c1c6 100644 (file)
@@ -25,6 +25,7 @@
 #include <sys/ioctl.h>
 
 #include "byte-order.h"
+#include "connectivity.h"
 #include "daemon.h"
 #include "dirs.h"
 #include "dpif.h"
@@ -35,6 +36,7 @@
 #include "ofpbuf.h"
 #include "packets.h"
 #include "route-table.h"
+#include "seq.h"
 #include "shash.h"
 #include "socket-util.h"
 #include "vlog.h"
@@ -52,7 +54,6 @@ struct netdev_vport {
     /* Protects all members below. */
     struct ovs_mutex mutex;
 
-    unsigned int change_seq;
     uint8_t etheraddr[ETH_ADDR_LEN];
     struct netdev_stats stats;
 
@@ -71,8 +72,6 @@ struct vport_class {
 static int netdev_vport_construct(struct netdev *);
 static int get_patch_config(const struct netdev *netdev, struct smap *args);
 static int get_tunnel_config(const struct netdev *, struct smap *args);
-static void netdev_vport_changed(struct netdev_vport *netdev)
-    OVS_REQUIRES(netdev->mutex);
 
 static bool
 is_vport_class(const struct netdev_class *class)
@@ -180,7 +179,6 @@ netdev_vport_construct(struct netdev *netdev_)
     struct netdev_vport *netdev = netdev_vport_cast(netdev_);
 
     ovs_mutex_init(&netdev->mutex);
-    netdev->change_seq = 1;
     eth_addr_random(netdev->etheraddr);
 
     route_table_register();
@@ -213,8 +211,8 @@ netdev_vport_set_etheraddr(struct netdev *netdev_,
 
     ovs_mutex_lock(&netdev->mutex);
     memcpy(netdev->etheraddr, mac, ETH_ADDR_LEN);
-    netdev_vport_changed(netdev);
     ovs_mutex_unlock(&netdev->mutex);
+    seq_change(connectivity_seq_get());
 
     return 0;
 }
@@ -272,12 +270,6 @@ netdev_vport_update_flags(struct netdev *netdev OVS_UNUSED,
     return 0;
 }
 
-static unsigned int
-netdev_vport_change_seq(const struct netdev *netdev)
-{
-    return netdev_vport_cast(netdev)->change_seq;
-}
-
 static void
 netdev_vport_run(void)
 {
@@ -290,17 +282,6 @@ netdev_vport_wait(void)
     route_table_wait();
 }
 \f
-/* Helper functions. */
-
-static void
-netdev_vport_changed(struct netdev_vport *ndv)
-{
-    ndv->change_seq++;
-    if (!ndv->change_seq) {
-        ndv->change_seq++;
-    }
-}
-\f
 /* Code specific to tunnel types. */
 
 static ovs_be64
@@ -503,7 +484,7 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args)
 
     ovs_mutex_lock(&dev->mutex);
     dev->tnl_cfg = tnl_cfg;
-    netdev_vport_changed(dev);
+    seq_change(connectivity_seq_get());
     ovs_mutex_unlock(&dev->mutex);
 
     return 0;
@@ -677,7 +658,7 @@ set_patch_config(struct netdev *dev_, const struct smap *args)
     ovs_mutex_lock(&dev->mutex);
     free(dev->peer);
     dev->peer = xstrdup(peer);
-    netdev_vport_changed(dev);
+    seq_change(connectivity_seq_get());
     ovs_mutex_unlock(&dev->mutex);
 
     return 0;
@@ -750,8 +731,6 @@ get_stats(const struct netdev *netdev, struct netdev_stats *stats)
                                                             \
     netdev_vport_update_flags,                              \
                                                             \
-    netdev_vport_change_seq,                                \
-                                                            \
     NULL,                   /* rx_alloc */                  \
     NULL,                   /* rx_construct */              \
     NULL,                   /* rx_destruct */               \
index 74f5f53..25d32db 100644 (file)
@@ -24,6 +24,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "connectivity.h"
 #include "coverage.h"
 #include "dpif.h"
 #include "dynamic-string.h"
@@ -36,6 +37,7 @@
 #include "openflow/openflow.h"
 #include "packets.h"
 #include "poll-loop.h"
+#include "seq.h"
 #include "shash.h"
 #include "smap.h"
 #include "sset.h"
@@ -333,6 +335,7 @@ netdev_open(const char *name, const char *type, struct netdev **netdevp)
                     int old_ref_cnt;
 
                     atomic_add(&rc->ref_cnt, 1, &old_ref_cnt);
+                    seq_change(connectivity_seq_get());
                 } else {
                     free(netdev->name);
                     ovs_assert(list_is_empty(&netdev->saved_flags_list));
@@ -388,13 +391,19 @@ netdev_set_config(struct netdev *netdev, const struct smap *args)
 {
     if (netdev->netdev_class->set_config) {
         const struct smap no_args = SMAP_INITIALIZER(&no_args);
-        return netdev->netdev_class->set_config(netdev,
-                                                args ? args : &no_args);
+        int error;
+
+        error = netdev->netdev_class->set_config(netdev,
+                                                 args ? args : &no_args);
+        if (error) {
+            VLOG_WARN("%s: could not set configuration (%s)",
+                      netdev_get_name(netdev), ovs_strerror(error));
+        }
+        return error;
     } else if (args && !smap_is_empty(args)) {
         VLOG_WARN("%s: arguments provided to device that is not configurable",
                   netdev_get_name(netdev));
     }
-
     return 0;
 }
 
@@ -1496,18 +1505,6 @@ netdev_dump_queue_stats(const struct netdev *netdev,
             : EOPNOTSUPP);
 }
 
-/* Returns a sequence number which indicates changes in one of 'netdev''s
- * properties.  The returned sequence will be nonzero so that callers have a
- * value which they may use as a reset when tracking 'netdev'.
- *
- * The returned sequence number will change whenever 'netdev''s flags,
- * features, ethernet address, or carrier changes.  It may change for other
- * reasons as well, or no reason at all. */
-unsigned int
-netdev_change_seq(const struct netdev *netdev)
-{
-    return netdev->netdev_class->change_seq(netdev);
-}
 \f
 /* Returns the class type of 'netdev'.
  *
index bafa50e..410c35b 100644 (file)
@@ -313,8 +313,6 @@ typedef void netdev_dump_queue_stats_cb(unsigned int queue_id,
 int netdev_dump_queue_stats(const struct netdev *,
                             netdev_dump_queue_stats_cb *, void *aux);
 
-unsigned int netdev_change_seq(const struct netdev *netdev);
-
 #ifdef  __cplusplus
 }
 #endif
index aea1f29..ada429e 100644 (file)
@@ -643,7 +643,7 @@ min_attr_len(enum nl_attr_type type)
     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();
+    case N_NL_ATTR_TYPES: default: OVS_NOT_REACHED();
     }
 }
 
@@ -661,7 +661,7 @@ max_attr_len(enum nl_attr_type type)
     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();
+    case N_NL_ATTR_TYPES: default: OVS_NOT_REACHED();
     }
 }
 
index af3370d..5b77fa9 100644 (file)
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "dpif.h"
 #include "netlink.h"
 #include "ofpbuf.h"
 #include "odp-util.h"
@@ -29,7 +30,8 @@
 #include "util.h"
 
 static void
-odp_eth_set_addrs(struct ofpbuf *packet, const struct ovs_key_ethernet *eth_key)
+odp_eth_set_addrs(struct ofpbuf *packet,
+                  const struct ovs_key_ethernet *eth_key)
 {
     struct eth_header *eh = packet->l2;
 
@@ -60,7 +62,7 @@ set_arp(struct ofpbuf *packet, const struct ovs_key_arp *arp_key)
 
 static void
 odp_execute_set_action(struct ofpbuf *packet, const struct nlattr *a,
-                       struct flow *flow)
+                       struct pkt_metadata *md)
 {
     enum ovs_key_attr type = nl_attr_type(a);
     const struct ovs_key_ipv4 *ipv4_key;
@@ -71,15 +73,15 @@ odp_execute_set_action(struct ofpbuf *packet, const struct nlattr *a,
 
     switch (type) {
     case OVS_KEY_ATTR_PRIORITY:
-        flow->skb_priority = nl_attr_get_u32(a);
+        md->skb_priority = nl_attr_get_u32(a);
         break;
 
     case OVS_KEY_ATTR_TUNNEL:
-        odp_set_tunnel_action(a, &flow->tunnel);
+        odp_set_tunnel_action(a, &md->tunnel);
         break;
 
     case OVS_KEY_ATTR_SKB_MARK:
-        flow->pkt_mark = nl_attr_get_u32(a);
+        md->pkt_mark = nl_attr_get_u32(a);
         break;
 
     case OVS_KEY_ATTR_ETHERNET:
@@ -134,19 +136,19 @@ odp_execute_set_action(struct ofpbuf *packet, const struct nlattr *a,
     case OVS_KEY_ATTR_TCP_FLAGS:
     case __OVS_KEY_ATTR_MAX:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
 static void
-odp_execute_sample(void *dp, struct ofpbuf *packet, struct flow *key,
+odp_execute_actions__(void *dp, struct ofpbuf *packet, struct pkt_metadata *,
+                      const struct nlattr *actions, size_t actions_len,
+                      odp_execute_cb dp_execute_action, bool more_actions);
+
+static void
+odp_execute_sample(void *dp, struct ofpbuf *packet, struct pkt_metadata *md,
                    const struct nlattr *action,
-                   void (*output)(void *dp, struct ofpbuf *packet,
-                                  const struct flow *key,
-                                  odp_port_t out_port),
-                   void (*userspace)(void *dp, struct ofpbuf *packet,
-                                     const struct flow *key,
-                                     const struct nlattr *action))
+                   odp_execute_cb dp_execute_action, bool more_actions)
 {
     const struct nlattr *subactions = NULL;
     const struct nlattr *a;
@@ -169,23 +171,19 @@ odp_execute_sample(void *dp, struct ofpbuf *packet, struct flow *key,
         case OVS_SAMPLE_ATTR_UNSPEC:
         case __OVS_SAMPLE_ATTR_MAX:
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
 
-    odp_execute_actions(dp, packet, key, nl_attr_get(subactions),
-                        nl_attr_get_size(subactions), output, userspace);
+    odp_execute_actions__(dp, packet, md, nl_attr_get(subactions),
+                          nl_attr_get_size(subactions), dp_execute_action,
+                          more_actions);
 }
 
-void
-odp_execute_actions(void *dp, struct ofpbuf *packet, struct flow *key,
-                    const struct nlattr *actions, size_t actions_len,
-                    void (*output)(void *dp, struct ofpbuf *packet,
-                                   const struct flow *key,
-                                   odp_port_t out_port),
-                    void (*userspace)(void *dp, struct ofpbuf *packet,
-                                      const struct flow *key,
-                                      const struct nlattr *action))
+static void
+odp_execute_actions__(void *dp, struct ofpbuf *packet, struct pkt_metadata *md,
+                      const struct nlattr *actions, size_t actions_len,
+                      odp_execute_cb dp_execute_action, bool more_actions)
 {
     const struct nlattr *a;
     unsigned int left;
@@ -194,18 +192,16 @@ odp_execute_actions(void *dp, struct ofpbuf *packet, struct flow *key,
         int type = nl_attr_type(a);
 
         switch ((enum ovs_action_attr) type) {
+            /* These only make sense in the context of a datapath. */
         case OVS_ACTION_ATTR_OUTPUT:
-            if (output) {
-                output(dp, packet, key, u32_to_odp(nl_attr_get_u32(a)));
-            }
-            break;
-
-        case OVS_ACTION_ATTR_USERSPACE: {
-            if (userspace) {
-                userspace(dp, packet, key, a);
+        case OVS_ACTION_ATTR_USERSPACE:
+            if (dp_execute_action) {
+                /* Allow 'dp_execute_action' to steal the packet data if we do
+                 * not need it any more. */
+                bool steal = !more_actions && left <= NLA_ALIGN(a->nla_len);
+                dp_execute_action(dp, packet, md, a, steal);
             }
             break;
-        }
 
         case OVS_ACTION_ATTR_PUSH_VLAN: {
             const struct ovs_action_push_vlan *vlan = nl_attr_get(a);
@@ -228,16 +224,26 @@ odp_execute_actions(void *dp, struct ofpbuf *packet, struct flow *key,
             break;
 
         case OVS_ACTION_ATTR_SET:
-            odp_execute_set_action(packet, nl_attr_get(a), key);
+            odp_execute_set_action(packet, nl_attr_get(a), md);
             break;
 
         case OVS_ACTION_ATTR_SAMPLE:
-            odp_execute_sample(dp, packet, key, a, output, userspace);
+            odp_execute_sample(dp, packet, md, a, dp_execute_action,
+                               more_actions || left > NLA_ALIGN(a->nla_len));
             break;
 
         case OVS_ACTION_ATTR_UNSPEC:
         case __OVS_ACTION_ATTR_MAX:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
 }
+
+void
+odp_execute_actions(void *dp, struct ofpbuf *packet, struct pkt_metadata *md,
+                    const struct nlattr *actions, size_t actions_len,
+                    odp_execute_cb dp_execute_action)
+{
+    odp_execute_actions__(dp, packet, md, actions, actions_len,
+                          dp_execute_action, false);
+}
index 5d9fa90..670e8ea 100644 (file)
 #ifndef EXECUTE_ACTIONS_H
 #define EXECUTE_ACTIONS_H 1
 
+#include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
 #include "openvswitch/types.h"
 
-struct flow;
 struct nlattr;
 struct ofpbuf;
+struct pkt_metadata;
 
+typedef void (*odp_execute_cb)(void *dp, struct ofpbuf *packet,
+                               const struct pkt_metadata *,
+                               const struct nlattr *action, bool may_steal);
+
+/* Actions that need to be executed in the context of a datapath are handed
+ * to 'dp_execute_action', if non-NULL.  Currently this is called only for
+ * actions OVS_ACTION_ATTR_OUTPUT and OVS_ACTION_ATTR_USERSPACE so
+ * 'dp_execute_action' needs to handle only these. */
 void
-odp_execute_actions(void *dp, struct ofpbuf *packet, struct flow *key,
+odp_execute_actions(void *dp, struct ofpbuf *packet, struct pkt_metadata *,
                     const struct nlattr *actions, size_t actions_len,
-                    void (*output)(void *dp, struct ofpbuf *packet,
-                                   const struct flow *key,
-                                   odp_port_t out_port),
-                    void (*userspace)(void *dp, struct ofpbuf *packet,
-                                      const struct flow *key,
-                                      const struct nlattr *action));
+                    odp_execute_cb dp_execute_action);
 #endif
index 6dbc213..873e05a 100644 (file)
@@ -26,6 +26,7 @@
 #include <string.h>
 #include "byte-order.h"
 #include "coverage.h"
+#include "dpif.h"
 #include "dynamic-string.h"
 #include "flow.h"
 #include "netlink.h"
@@ -845,7 +846,7 @@ odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
         return ODP_FIT_ERROR;
     }
     if (unknown) {
-            return ODP_FIT_TOO_MUCH;
+        return ODP_FIT_TOO_MUCH;
     }
     return ODP_FIT_PERFECT;
 }
@@ -2596,6 +2597,74 @@ odp_flow_key_from_mask(struct ofpbuf *buf, const struct flow *mask,
     odp_flow_key_from_flow__(buf, mask, flow, u32_to_odp(odp_in_port_mask));
 }
 
+/* Generate ODP flow key from the given packet metadata */
+void
+odp_key_from_pkt_metadata(struct ofpbuf *buf, const struct pkt_metadata *md)
+{
+    nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, md->skb_priority);
+
+    if (md->tunnel.ip_dst) {
+        tun_key_to_attr(buf, &md->tunnel);
+    }
+
+    nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, md->pkt_mark);
+
+    /* Add an ingress port attribute if 'odp_in_port' is not the magical
+     * value "ODPP_NONE". */
+    if (md->in_port != ODPP_NONE) {
+        nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, md->in_port);
+    }
+}
+
+/* Generate packet metadata from the given ODP flow key. */
+void
+odp_key_to_pkt_metadata(const struct nlattr *key, size_t key_len,
+                        struct pkt_metadata *md)
+{
+    const struct nlattr *nla;
+    size_t left;
+    uint32_t wanted_attrs = 1u << OVS_KEY_ATTR_PRIORITY |
+        1u << OVS_KEY_ATTR_SKB_MARK | 1u << OVS_KEY_ATTR_TUNNEL |
+        1u << OVS_KEY_ATTR_IN_PORT;
+
+    memset(md, 0, sizeof *md);
+    md->in_port = ODPP_NONE;
+
+    NL_ATTR_FOR_EACH (nla, left, key, key_len) {
+        uint16_t type = nl_attr_type(nla);
+        size_t len = nl_attr_get_size(nla);
+        int expected_len = odp_flow_key_attr_len(type);
+
+        if (len != expected_len && expected_len >= 0) {
+            continue;
+        }
+
+        if (type == OVS_KEY_ATTR_PRIORITY) {
+            md->skb_priority = nl_attr_get_u32(nla);
+            wanted_attrs &= ~(1u << OVS_KEY_ATTR_PRIORITY);
+        } else if (type == OVS_KEY_ATTR_SKB_MARK) {
+            md->pkt_mark = nl_attr_get_u32(nla);
+            wanted_attrs &= ~(1u << OVS_KEY_ATTR_SKB_MARK);
+        } else if (type == OVS_KEY_ATTR_TUNNEL) {
+            enum odp_key_fitness res;
+
+            res = odp_tun_key_from_attr(nla, &md->tunnel);
+            if (res == ODP_FIT_ERROR) {
+                memset(&md->tunnel, 0, sizeof md->tunnel);
+            } else if (res == ODP_FIT_PERFECT) {
+                wanted_attrs &= ~(1u << OVS_KEY_ATTR_TUNNEL);
+            }
+        } else if (type == OVS_KEY_ATTR_IN_PORT) {
+            md->in_port = nl_attr_get_odp_port(nla);
+            wanted_attrs &= ~(1u << OVS_KEY_ATTR_IN_PORT);
+        }
+
+        if (!wanted_attrs) {
+            return; /* Have everything. */
+        }
+    }
+}
+
 uint32_t
 odp_flow_key_hash(const struct nlattr *key, size_t key_len)
 {
@@ -3035,7 +3104,9 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
     if (!is_mask && !(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN))) {
         return ODP_FIT_TOO_LITTLE;
     } else {
-        tci = nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN]);
+        tci = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)
+               ? nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN])
+               : htons(0));
         if (!is_mask) {
             if (tci == htons(0)) {
                 /* Corner case for a truncated 802.1Q header. */
@@ -3146,8 +3217,9 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
         return ODP_FIT_ERROR;
     }
 
-    if ((is_mask && (src_flow->vlan_tci & htons(VLAN_CFI))) ||
-        (!is_mask && src_flow->dl_type == htons(ETH_TYPE_VLAN))) {
+    if (is_mask
+        ? (src_flow->vlan_tci & htons(VLAN_CFI)) != 0
+        : src_flow->dl_type == htons(ETH_TYPE_VLAN)) {
         return parse_8021q_onward(attrs, present_attrs, out_of_range_attr,
                                   expected_attrs, flow, key, key_len, src_flow);
     }
@@ -3383,7 +3455,7 @@ commit_mpls_action(const struct flow *flow, struct flow *base,
         break;
     }
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     base->dl_type = flow->dl_type;
index 821b2c4..2b62614 100644 (file)
@@ -34,6 +34,7 @@ struct flow_wildcards;
 struct nlattr;
 struct ofpbuf;
 struct simap;
+struct pkt_metadata;
 
 #define SLOW_PATH_REASONS                                               \
     /* These reasons are mutually exclusive. */                         \
@@ -149,6 +150,12 @@ void odp_flow_key_from_mask(struct ofpbuf *, const struct flow *mask,
 
 uint32_t odp_flow_key_hash(const struct nlattr *, size_t);
 
+/* Estimated space needed for metadata. */
+enum { ODP_KEY_METADATA_SIZE = 9 * 8 };
+void odp_key_from_pkt_metadata(struct ofpbuf *, const struct pkt_metadata *);
+void odp_key_to_pkt_metadata(const struct nlattr *key, size_t key_len,
+                              struct pkt_metadata *md);
+
 /* How well a kernel-provided flow key (a sequence of OVS_KEY_ATTR_*
  * attributes) matches OVS userspace expectations.
  *
index a02f842..df7aebd 100644 (file)
@@ -324,7 +324,7 @@ decode_nxast_action(const union ofp_action *a, enum ofputil_action_code *code)
             } else {                                    \
                 return OFPERR_OFPBAC_BAD_LEN;           \
             }                                           \
-            NOT_REACHED();
+            OVS_NOT_REACHED();
 #include "ofp-util.def"
 
     case CONSTANT_HTONS(NXAST_SNAT__OBSOLETE):
@@ -380,7 +380,7 @@ ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code,
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
 #define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
 #include "ofp-util.def"
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OFPUTIL_NXAST_RESUBMIT:
         resubmit_from_openflow(&a->resubmit, out);
@@ -527,7 +527,7 @@ ofpact_from_openflow10(const union ofp_action *a,
     case OFPUTIL_ACTION_INVALID:
 #define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
 #include "ofp-util.def"
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OFPUTIL_OFPAT10_OUTPUT:
         return output_from_openflow10(&a->output10, out);
@@ -764,7 +764,7 @@ decode_openflow11_action(const union ofp_action *a,
             } else {                                    \
                 return OFPERR_OFPBAC_BAD_LEN;           \
             }                                           \
-            NOT_REACHED();
+            OVS_NOT_REACHED();
 #include "ofp-util.def"
 
     default:
@@ -1087,7 +1087,7 @@ set_field_to_openflow(const struct ofpact_set_field *sf,
     } else if (oh->version == OFP10_VERSION) {
         set_field_to_openflow10(sf, openflow);
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1145,7 +1145,7 @@ ofpact_from_openflow11(const union ofp_action *a, enum ofp_version version,
     case OFPUTIL_ACTION_INVALID:
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
 #include "ofp-util.def"
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OFPUTIL_OFPAT11_OUTPUT:
         return output_from_openflow11(&a->ofp11_output, out);
@@ -1337,7 +1337,7 @@ ofpact_is_set_action(const struct ofpact *a)
     case OFPACT_WRITE_METADATA:
         return false;
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1404,7 +1404,7 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a)
     case OFPACT_WRITE_METADATA:
         return false;
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -2119,7 +2119,7 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
         return 0;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -2481,7 +2481,7 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_GOTO_TABLE:
     case OFPACT_METER:
     case OFPACT_SET_FIELD:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 \f
@@ -2782,7 +2782,7 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_WRITE_ACTIONS:
     case OFPACT_GOTO_TABLE:
     case OFPACT_METER:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OFPACT_GROUP:
         ofputil_put_OFPAT11_GROUP(out)->group_id =
index 470a371..00cba6a 100644 (file)
@@ -494,9 +494,9 @@ struct ofpact_learn {
     uint16_t idle_timeout;      /* Idle time before discarding (seconds). */
     uint16_t hard_timeout;      /* Max time before discarding (seconds). */
     uint16_t priority;          /* Priority level of flow entry. */
+    uint8_t table_id;           /* Table to insert flow entry. */
     uint64_t cookie;            /* Cookie for new flow. */
     enum ofputil_flow_mod_flags flags;
-    uint8_t table_id;           /* Table to insert flow entry. */
     uint16_t fin_idle_timeout;  /* Idle timeout after FIN, if nonzero. */
     uint16_t fin_hard_timeout;  /* Hard timeout after FIN, if nonzero. */
 
index 9f1b453..4f07d0e 100644 (file)
@@ -693,7 +693,7 @@ ofpraw_put__(enum ofpraw raw, uint8_t version, ovs_be32 xid,
                 nsm->subtype = htonl(hdrs->subtype);
                 memset(nsm->pad, 0, sizeof nsm->pad);
             } else {
-                NOT_REACHED();
+                OVS_NOT_REACHED();
             }
         }
     } else if (version != OFP10_VERSION
@@ -714,7 +714,7 @@ ofpraw_put__(enum ofpraw raw, uint8_t version, ovs_be32 xid,
 
                 nsm->subtype = htonl(hdrs->subtype);
             } else {
-                NOT_REACHED();
+                OVS_NOT_REACHED();
             }
         }
     }
@@ -757,7 +757,7 @@ ofpraw_stats_request_to_reply(enum ofpraw raw, uint8_t version)
         hdrs.type = OFPT11_STATS_REPLY;
         break;
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     error = ofpraw_from_ofphdrs(&reply_raw, &hdrs);
@@ -941,7 +941,7 @@ ofpmp_flags__(const struct ofp_header *oh)
     case OFP13_VERSION:
         return &((struct ofp11_stats_msg *) oh)->flags;
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
index 5b07d14..251adfa 100644 (file)
@@ -212,10 +212,10 @@ parse_output(const char *arg, struct ofpbuf *ofpacts)
         struct ofpact_output *output;
 
         output = ofpact_put_OUTPUT(ofpacts);
-        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);
         }
+        output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
         return NULL;
     }
 }
@@ -644,7 +644,7 @@ parse_named_action(enum ofputil_action_code code,
 
     switch (code) {
     case OFPUTIL_ACTION_INVALID:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OFPUTIL_OFPAT10_OUTPUT:
     case OFPUTIL_OFPAT11_OUTPUT:
@@ -765,7 +765,7 @@ parse_named_action(enum ofputil_action_code code,
         break;
 
     case OFPUTIL_OFPAT11_DEC_NW_TTL:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OFPUTIL_OFPAT10_SET_TP_SRC:
     case OFPUTIL_OFPAT11_SET_TP_SRC:
@@ -833,7 +833,7 @@ parse_named_action(enum ofputil_action_code code,
     case OFPUTIL_NXAST_RESUBMIT_TABLE:
     case OFPUTIL_NXAST_OUTPUT_REG:
     case OFPUTIL_NXAST_DEC_TTL_CNT_IDS:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OFPUTIL_NXAST_LEARN:
         error = learn_parse(arg, ofpacts);
@@ -1021,7 +1021,7 @@ parse_named_instruction(enum ovs_instruction_type type,
 
     switch (type) {
     case OVSINST_OFPIT11_APPLY_ACTIONS:
-        NOT_REACHED();  /* This case is handled by str_to_inst_ofpacts() */
+        OVS_NOT_REACHED();  /* This case is handled by str_to_inst_ofpacts() */
         break;
 
     case OVSINST_OFPIT11_WRITE_ACTIONS: {
@@ -1246,7 +1246,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     match_init_catchall(&fm->match);
@@ -1500,7 +1500,7 @@ parse_ofp_meter_mod_str__(struct ofputil_meter_mod *mm, char *string,
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     mm->command = command;
@@ -2146,7 +2146,7 @@ parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, uint16_t command,
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     memset(gm, 0, sizeof *gm);
index 13705d0..4c89b36 100644 (file)
@@ -540,7 +540,7 @@ ofp_print_switch_features(struct ds *string, const struct ofp_header *oh)
     case OFP13_VERSION:
         return; /* no ports in ofp13_switch_features */
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     ofp_print_phy_ports(string, oh->version, &b);
@@ -860,10 +860,26 @@ static void
 ofp_print_duration(struct ds *string, unsigned int sec, unsigned int nsec)
 {
     ds_put_format(string, "%u", sec);
+
+    /* If there are no fractional seconds, don't print any decimals.
+     *
+     * If the fractional seconds can be expressed exactly as milliseconds,
+     * print 3 decimals.  Open vSwitch provides millisecond precision for most
+     * time measurements, so printing 3 decimals every time makes it easier to
+     * spot real changes in flow dumps that refresh themselves quickly.
+     *
+     * If the fractional seconds are more precise than milliseconds, print the
+     * number of decimals needed to express them exactly.
+     */
     if (nsec > 0) {
-        ds_put_format(string, ".%09u", nsec);
-        while (string->string[string->length - 1] == '0') {
-            string->length--;
+        unsigned int msec = nsec / 1000000;
+        if (msec * 1000000 == nsec) {
+            ds_put_format(string, ".%03u", msec);
+        } else {
+            ds_put_format(string, ".%09u", nsec);
+            while (string->string[string->length - 1] == '0') {
+                string->length--;
+            }
         }
     }
     ds_put_char(string, 's');
@@ -1789,7 +1805,7 @@ ofp_print_ofpst_table_reply(struct ds *string, const struct ofp_header *oh,
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1937,7 +1953,7 @@ ofp_print_role_generic(struct ds *string, enum ofp12_controller_role role,
         ds_put_cstr(string, "slave");
         break;
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     if (generation_id != UINT64_MAX) {
@@ -1987,7 +2003,7 @@ ofp_print_role_status_message(struct ds *string, const struct ofp_header *oh)
         ds_put_cstr(string, "experimenter_data_changed");
         break;
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
index 7fc4c7c..a0a372f 100644 (file)
@@ -573,7 +573,7 @@ ofputil_match_typical_len(enum ofputil_protocol protocol)
         return NXM_TYPICAL_LEN;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -595,7 +595,7 @@ ofputil_put_ofp11_match(struct ofpbuf *b, const struct match *match,
     case OFPUTIL_P_OF10_STD_TID:
     case OFPUTIL_P_OF10_NXM:
     case OFPUTIL_P_OF10_NXM_TID:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OFPUTIL_P_OF11_STD: {
         struct ofp11_match *om;
@@ -613,7 +613,7 @@ ofputil_put_ofp11_match(struct ofpbuf *b, const struct match *match,
         return oxm_put_match(b, match);
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 /* Given a 'dl_type' value in the format used in struct flow, returns the
@@ -715,7 +715,7 @@ ofputil_protocol_to_ofp_version(enum ofputil_protocol protocol)
         return OFP13_VERSION;
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 /* Returns a bitmap of OpenFlow versions that are supported by at
@@ -790,7 +790,7 @@ ofputil_protocol_set_tid(enum ofputil_protocol protocol, bool enable)
         return OFPUTIL_P_OF13_OXM;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -830,7 +830,7 @@ ofputil_protocol_set_base(enum ofputil_protocol cur,
         return ofputil_protocol_set_tid(OFPUTIL_P_OF13_OXM, tid);
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -918,7 +918,7 @@ ofputil_protocols_to_string(enum ofputil_protocol protocols)
                 goto match;
             }
         }
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     match: ;
     }
@@ -1071,7 +1071,7 @@ ofputil_version_to_string(enum ofp_version ofp_version)
     case OFP13_VERSION:
         return "OpenFlow13";
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1096,7 +1096,7 @@ ofputil_packet_in_format_to_string(enum nx_packet_in_format packet_in_format)
     case NXPIF_NXM:
         return "nxm";
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1305,11 +1305,11 @@ ofputil_encode_set_protocol(enum ofputil_protocol current,
         case OFPUTIL_P_OF13_OXM:
             /* 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();
+            OVS_NOT_REACHED();
 
         case OFPUTIL_P_OF10_STD_TID:
         case OFPUTIL_P_OF10_NXM_TID:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
 
@@ -1378,7 +1378,7 @@ ofputil_nx_flow_format_to_string(enum nx_flow_format flow_format)
     case NXFF_NXM:
         return "nxm";
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1638,7 +1638,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             fm->out_group = OFPG11_ANY;
             raw_flags = nfm->flags;
         } else {
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
 
         fm->modify_cookie = fm->new_cookie != OVS_BE64_MAX;
@@ -2161,7 +2161,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
     }
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     ofpmsg_update_length(msg);
@@ -2286,7 +2286,7 @@ ofputil_decode_queue_get_config_request(const struct ofp_header *oh,
         return ofputil_port_from_ofp11(qgcr11->port, port);
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 /* Constructs and returns the beginning of a reply to
@@ -2325,7 +2325,7 @@ ofputil_encode_queue_get_config_reply(const struct ofp_header *oh)
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return reply;
@@ -2407,7 +2407,7 @@ ofputil_decode_queue_get_config_reply(struct ofpbuf *reply, ofp_port_t *port)
         return ofputil_port_from_ofp11(qgcr11->port, port);
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 static enum ofperr
@@ -2543,7 +2543,7 @@ ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
 
     default:
         /* Hey, the caller lied. */
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -2614,7 +2614,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
     }
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return msg;
@@ -2797,7 +2797,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
         fs->byte_count = ntohll(nfs->byte_count);
         fs->flags = 0;
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     fs->ofpacts = ofpacts->data;
@@ -2905,7 +2905,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
         nfs->packet_count = htonll(fs->packet_count);
         nfs->byte_count = htonll(fs->byte_count);
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     ofpmp_postappend(replies, start_ofs);
@@ -3034,7 +3034,7 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
         fr->packet_count = ntohll(nfr->packet_count);
         fr->byte_count = ntohll(nfr->byte_count);
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return 0;
@@ -3118,7 +3118,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
     }
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return msg;
@@ -3238,7 +3238,7 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
 
         ofputil_decode_packet_in_finish(pin, &match, &b);
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return 0;
@@ -3420,7 +3420,7 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     ofpmsg_update_length(packet);
@@ -3516,7 +3516,7 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
             return error;
         }
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     if (ofp_to_u16(po->in_port) >= ofp_to_u16(OFPP_MAX)
@@ -3665,7 +3665,7 @@ ofputil_get_phy_port_size(enum ofp_version ofp_version)
     case OFP13_VERSION:
         return sizeof(struct ofp11_port);
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -3736,7 +3736,7 @@ ofputil_put_phy_port(enum ofp_version ofp_version,
     }
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -3765,7 +3765,7 @@ ofputil_append_port_desc_stats_reply(enum ofp_version ofp_version,
     }
 
     default:
-      NOT_REACHED();
+      OVS_NOT_REACHED();
     }
 }
 \f
@@ -3956,7 +3956,7 @@ ofputil_encode_switch_features(const struct ofputil_switch_features *features,
         raw = OFPRAW_OFPT13_FEATURES_REPLY;
         break;
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
     b = ofpraw_alloc_xid(raw, version, xid, 0);
     osf = ofpbuf_put_zeros(b, sizeof *osf);
@@ -3984,7 +3984,7 @@ ofputil_encode_switch_features(const struct ofputil_switch_features *features,
         }
         break;
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return b;
@@ -4057,7 +4057,7 @@ ofputil_encode_port_status(const struct ofputil_port_status *ps,
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     b = ofpraw_alloc_xid(raw, version, htonl(0), 0);
@@ -4150,7 +4150,7 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
         break;
     }
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return b;
@@ -4210,7 +4210,7 @@ ofputil_encode_table_mod(const struct ofputil_table_mod *pm,
         break;
     }
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return b;
@@ -4270,7 +4270,7 @@ ofputil_decode_role_message(const struct ofp_header *oh,
         rr->have_generation_id = false;
         rr->generation_id = 0;
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return 0;
@@ -4307,7 +4307,7 @@ ofputil_encode_role_reply(const struct ofp_header *request,
         nrr = ofpbuf_put_zeros(buf, sizeof *nrr);
         nrr->role = htonl(rr->role - 1);
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return buf;
@@ -4519,7 +4519,7 @@ ofputil_encode_table_stats_reply(const struct ofp12_table_stats stats[], int n,
             break;
 
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
 
@@ -4839,7 +4839,7 @@ ofputil_encode_packet_out(const struct ofputil_packet_out *po,
     }
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     if (po->buffer_id == UINT32_MAX) {
@@ -4892,7 +4892,7 @@ ofputil_encode_barrier_request(enum ofp_version ofp_version)
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return ofpraw_alloc(type, ofp_version, 0);
@@ -4908,7 +4908,7 @@ ofputil_frag_handling_to_string(enum ofp_config_flags flags)
     case OFPC_FRAG_NX_MATCH: return "nx-match";
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 bool
@@ -5162,7 +5162,7 @@ ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *b,
         return op ? ofputil_decode_ofp11_port(pp, op) : EOF;
     }
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -5217,7 +5217,7 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
 {
     switch (code) {
     case OFPUTIL_ACTION_INVALID:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME)                  \
     case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
@@ -5227,7 +5227,7 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
     case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
 #include "ofp-util.def"
     }
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME)                        \
@@ -5497,7 +5497,7 @@ ofputil_encode_dump_ports_request(enum ofp_version ofp_version, ofp_port_t port)
         break;
     }
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return request;
@@ -5581,7 +5581,7 @@ ofputil_append_port_stat(struct list *replies,
     }
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -5663,7 +5663,7 @@ ofputil_get_port_stats_size(enum ofp_version ofp_version)
     case OFP13_VERSION:
         return sizeof(struct ofp13_port_stats);
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -5730,7 +5730,7 @@ ofputil_decode_port_stats(struct ofputil_port_stats *ps, struct ofpbuf *msg)
         }
         return ofputil_port_stats_from_ofp10(ps, ps10);
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
  bad_len:
@@ -5761,7 +5761,7 @@ ofputil_decode_port_stats_request(const struct ofp_header *request,
     }
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -5803,7 +5803,7 @@ ofputil_encode_group_stats_request(enum ofp_version ofp_version,
         break;
     }
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return request;
@@ -5831,7 +5831,7 @@ ofputil_encode_group_desc_request(enum ofp_version ofp_version)
         break;
     }
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return request;
@@ -5901,7 +5901,7 @@ ofputil_append_group_stats(struct list *replies,
 
     case OFP10_VERSION:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -5924,7 +5924,7 @@ ofputil_encode_group_features_request(enum ofp_version ofp_version)
         break;
     }
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return request;
@@ -6040,7 +6040,7 @@ ofputil_decode_group_stats_reply(struct ofpbuf *msg,
             ogs11 = NULL;
         }
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     if (!ogs11) {
@@ -6277,7 +6277,7 @@ ofputil_encode_group_mod(enum ofp_version ofp_version,
     }
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return b;
@@ -6323,7 +6323,7 @@ ofputil_decode_group_mod(const struct ofp_header *oh,
             }
             break;
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
 
@@ -6357,7 +6357,7 @@ ofputil_decode_queue_stats_request(const struct ofp_header *request,
     }
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -6392,7 +6392,7 @@ ofputil_encode_queue_stats_request(enum ofp_version ofp_version,
         break;
     }
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return request;
@@ -6410,7 +6410,7 @@ ofputil_get_queue_stats_size(enum ofp_version ofp_version)
     case OFP13_VERSION:
         return sizeof(struct ofp13_queue_stats);
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -6524,7 +6524,7 @@ ofputil_decode_queue_stats(struct ofputil_queue_stats *qs, struct ofpbuf *msg)
         }
         return ofputil_queue_stats_from_ofp10(qs, qs10);
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
  bad_len:
@@ -6599,6 +6599,6 @@ ofputil_append_queue_stat(struct list *replies,
     }
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
index fef85e0..bf02b9f 100644 (file)
@@ -20,9 +20,9 @@
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
-#include "classifier.h"
 #include "compiler.h"
 #include "flow.h"
+#include "list.h"
 #include "match.h"
 #include "netdev.h"
 #include "openflow/nicira-ext.h"
@@ -321,11 +321,11 @@ struct ofputil_flow_stats {
     struct match match;
     ovs_be64 cookie;
     uint8_t table_id;
-    uint32_t duration_sec;
-    uint32_t duration_nsec;
     uint16_t priority;
     uint16_t idle_timeout;
     uint16_t hard_timeout;
+    uint32_t duration_sec;
+    uint32_t duration_nsec;
     int idle_age;               /* Seconds since last packet, -1 if unknown. */
     int hard_age;               /* Seconds since last change, -1 if unknown. */
     uint64_t packet_count;      /* Packet count, UINT64_MAX if unknown. */
@@ -359,8 +359,8 @@ enum ofperr ofputil_decode_aggregate_stats_reply(
 /* Flow removed message, independent of protocol. */
 struct ofputil_flow_removed {
     struct match match;
-    uint16_t priority;
     ovs_be64 cookie;
+    uint16_t priority;
     uint8_t reason;             /* One of OFPRR_*. */
     uint8_t table_id;           /* 255 if message didn't include table ID. */
     uint32_t duration_sec;
@@ -770,9 +770,9 @@ struct ofputil_flow_update {
     uint16_t idle_timeout;
     uint16_t hard_timeout;
     uint8_t table_id;
+    uint16_t priority;
     ovs_be64 cookie;
     struct match *match;
-    uint16_t priority;
     struct ofpact *ofpacts;
     size_t ofpacts_len;
 
index 30ae12e..0eed428 100644 (file)
@@ -276,7 +276,7 @@ ofpbuf_resize__(struct ofpbuf *b, size_t new_headroom, size_t new_tailroom)
         break;
 
     case OFPBUF_STACK:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OFPBUF_STUB:
         b->source = OFPBUF_MALLOC;
@@ -285,7 +285,7 @@ ofpbuf_resize__(struct ofpbuf *b, size_t new_headroom, size_t new_tailroom)
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     b->allocated = new_allocated;
@@ -359,6 +359,24 @@ ofpbuf_padto(struct ofpbuf *b, size_t length)
     }
 }
 
+/* Shifts all of the data within the allocated space in 'b' by 'delta' bytes.
+ * For example, a 'delta' of 1 would cause each byte of data to move one byte
+ * forward (from address 'p' to 'p+1'), and a 'delta' of -1 would cause each
+ * byte to move one byte backward (from 'p' to 'p-1'). */
+void
+ofpbuf_shift(struct ofpbuf *b, int delta)
+{
+    ovs_assert(delta > 0 ? delta <= ofpbuf_tailroom(b)
+               : delta < 0 ? -delta <= ofpbuf_headroom(b)
+               : true);
+
+    if (delta != 0) {
+        char *dst = (char *) b->data + delta;
+        memmove(dst, b->data, b->size);
+        b->data = dst;
+    }
+}
+
 /* Appends 'size' bytes of data to the tail end of 'b', reallocating and
  * copying its data if necessary.  Returns a pointer to the first byte of the
  * new data, which is left uninitialized. */
@@ -431,6 +449,17 @@ ofpbuf_reserve(struct ofpbuf *b, size_t size)
     b->data = (char*)b->data + size;
 }
 
+/* Reserves 'size' bytes of headroom so that they can be later allocated with
+ * ofpbuf_push_uninit() without reallocating the ofpbuf. */
+void
+ofpbuf_reserve_with_tailroom(struct ofpbuf *b, size_t headroom,
+                             size_t tailroom)
+{
+    ovs_assert(!b->size);
+    ofpbuf_prealloc_tailroom(b, headroom + tailroom);
+    b->data = (char*)b->data + headroom;
+}
+
 /* Prefixes 'size' bytes to the head end of 'b', reallocating and copying its
  * data if necessary.  Returns a pointer to the first byte of the data's
  * location in the ofpbuf.  The new data is left uninitialized. */
index 0c12162..7407d8b 100644 (file)
@@ -82,6 +82,8 @@ void *ofpbuf_put_zeros(struct ofpbuf *, size_t);
 void *ofpbuf_put(struct ofpbuf *, const void *, size_t);
 char *ofpbuf_put_hex(struct ofpbuf *, const char *s, size_t *n);
 void ofpbuf_reserve(struct ofpbuf *, size_t);
+void ofpbuf_reserve_with_tailroom(struct ofpbuf *b, size_t headroom,
+                                  size_t tailroom);
 void *ofpbuf_push_uninit(struct ofpbuf *b, size_t);
 void *ofpbuf_push_zeros(struct ofpbuf *, size_t);
 void *ofpbuf_push(struct ofpbuf *b, const void *, size_t);
@@ -92,6 +94,7 @@ void ofpbuf_prealloc_headroom(struct ofpbuf *, size_t);
 void ofpbuf_prealloc_tailroom(struct ofpbuf *, size_t);
 void ofpbuf_trim(struct ofpbuf *);
 void ofpbuf_padto(struct ofpbuf *, size_t);
+void ofpbuf_shift(struct ofpbuf *, int);
 
 void ofpbuf_clear(struct ofpbuf *);
 void *ofpbuf_pull(struct ofpbuf *, size_t);
index 1a633cf..aa3ad15 100644 (file)
@@ -326,7 +326,7 @@ parse_cpuinfo(long int *n_cores)
 
     stream = fopen(file_name, "r");
     if (!stream) {
-        VLOG_WARN("%s: open failed (%s)", file_name, ovs_strerror(errno));
+        VLOG_DBG("%s: open failed (%s)", file_name, ovs_strerror(errno));
         return;
     }
 
@@ -371,7 +371,7 @@ parse_cpuinfo(long int *n_cores)
  * Tries not to count hyper-threads, but may be inaccurate - particularly on
  * platforms that do not provide /proc/cpuinfo, but also if /proc/cpuinfo is
  * formatted different to the layout that parse_cpuinfo() expects. */
-unsigned int
+int
 count_cpu_cores(void)
 {
     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
index c6a7142..b6d973f 100644 (file)
@@ -276,7 +276,10 @@ void xpthread_join(pthread_t, void **);
         if (!value) {                                                   \
             static const NAME##_type initial_value = __VA_ARGS__;       \
                                                                         \
-            value = xmalloc(sizeof *value);                             \
+            value = malloc(sizeof *value);                              \
+            if (value == NULL) {                                        \
+                out_of_memory();                                        \
+            }                                                           \
             *value = initial_value;                                     \
             xpthread_setspecific(NAME##_key, value);                    \
         }                                                               \
@@ -313,7 +316,10 @@ void xpthread_join(pthread_t, void **);
         if (!value) {                                                   \
             static const NAME##_type initial_value = __VA_ARGS__;       \
                                                                         \
-            value = xmalloc(sizeof *value);                             \
+            value = malloc(sizeof *value);                              \
+            if (value == NULL) {                                        \
+                out_of_memory();                                        \
+            }                                                           \
             *value = initial_value;                                     \
             xpthread_setspecific(NAME##_key, value);                    \
         }                                                               \
@@ -499,6 +505,6 @@ bool may_fork(void);
 \f
 /* Useful functions related to threading. */
 
-unsigned int count_cpu_cores(void);
+int count_cpu_cores(void);
 
 #endif /* ovs-thread.h */
index 4e918a6..b4b28db 100644 (file)
@@ -58,7 +58,7 @@ ovsdb_atom_init_default(union ovsdb_atom *atom, enum ovsdb_atomic_type type)
 {
     switch (type) {
     case OVSDB_TYPE_VOID:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OVSDB_TYPE_INTEGER:
         atom->integer = 0;
@@ -82,7 +82,7 @@ ovsdb_atom_init_default(union ovsdb_atom *atom, enum ovsdb_atomic_type type)
 
     case OVSDB_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -123,7 +123,7 @@ ovsdb_atom_is_default(const union ovsdb_atom *atom,
 {
     switch (type) {
     case OVSDB_TYPE_VOID:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OVSDB_TYPE_INTEGER:
         return atom->integer == 0;
@@ -142,7 +142,7 @@ ovsdb_atom_is_default(const union ovsdb_atom *atom,
 
     case OVSDB_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -156,7 +156,7 @@ ovsdb_atom_clone(union ovsdb_atom *new, const union ovsdb_atom *old,
 {
     switch (type) {
     case OVSDB_TYPE_VOID:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OVSDB_TYPE_INTEGER:
         new->integer = old->integer;
@@ -180,7 +180,7 @@ ovsdb_atom_clone(union ovsdb_atom *new, const union ovsdb_atom *old,
 
     case OVSDB_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -201,7 +201,7 @@ ovsdb_atom_hash(const union ovsdb_atom *atom, enum ovsdb_atomic_type type,
 {
     switch (type) {
     case OVSDB_TYPE_VOID:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OVSDB_TYPE_INTEGER:
         return hash_int(atom->integer, basis);
@@ -220,7 +220,7 @@ ovsdb_atom_hash(const union ovsdb_atom *atom, enum ovsdb_atomic_type type,
 
     case OVSDB_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -233,7 +233,7 @@ ovsdb_atom_compare_3way(const union ovsdb_atom *a,
 {
     switch (type) {
     case OVSDB_TYPE_VOID:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OVSDB_TYPE_INTEGER:
         return a->integer < b->integer ? -1 : a->integer > b->integer;
@@ -252,7 +252,7 @@ ovsdb_atom_compare_3way(const union ovsdb_atom *a,
 
     case OVSDB_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -353,7 +353,7 @@ ovsdb_atom_from_json__(union ovsdb_atom *atom,
 
     switch (type) {
     case OVSDB_TYPE_VOID:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OVSDB_TYPE_INTEGER:
         if (json->type == JSON_INTEGER) {
@@ -394,7 +394,7 @@ ovsdb_atom_from_json__(union ovsdb_atom *atom,
 
     case OVSDB_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return ovsdb_syntax_error(json, NULL, "expected %s",
@@ -443,7 +443,7 @@ ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type)
 {
     switch (type) {
     case OVSDB_TYPE_VOID:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OVSDB_TYPE_INTEGER:
         return json_integer_create(atom->integer);
@@ -463,7 +463,7 @@ ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type)
 
     case OVSDB_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -476,7 +476,7 @@ ovsdb_atom_json_length(const union ovsdb_atom *atom,
 
     switch (type) {
     case OVSDB_TYPE_VOID:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OVSDB_TYPE_INTEGER:
         json.type = JSON_INTEGER;
@@ -502,7 +502,7 @@ ovsdb_atom_json_length(const union ovsdb_atom *atom,
 
     case OVSDB_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return json_serialized_length(&json);
@@ -517,7 +517,7 @@ ovsdb_atom_from_string__(union ovsdb_atom *atom,
 
     switch (type) {
     case OVSDB_TYPE_VOID:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OVSDB_TYPE_INTEGER: {
         long long int integer;
@@ -585,7 +585,7 @@ ovsdb_atom_from_string__(union ovsdb_atom *atom,
 
     case OVSDB_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return NULL;
@@ -671,7 +671,7 @@ ovsdb_atom_to_string(const union ovsdb_atom *atom, enum ovsdb_atomic_type type,
 {
     switch (type) {
     case OVSDB_TYPE_VOID:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OVSDB_TYPE_INTEGER:
         ds_put_format(out, "%"PRId64, atom->integer);
@@ -703,7 +703,7 @@ ovsdb_atom_to_string(const union ovsdb_atom *atom, enum ovsdb_atomic_type type,
 
     case OVSDB_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -784,7 +784,7 @@ ovsdb_atom_check_constraints(const union ovsdb_atom *atom,
 
     switch (base->type) {
     case OVSDB_TYPE_VOID:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OVSDB_TYPE_INTEGER:
         if (atom->integer >= base->u.integer.min
@@ -809,7 +809,7 @@ ovsdb_atom_check_constraints(const union ovsdb_atom *atom,
                                "value %"PRId64,
                                atom->integer, base->u.integer.max);
         }
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OVSDB_TYPE_REAL:
         if (atom->real >= base->u.real.min && atom->real <= base->u.real.max) {
@@ -836,7 +836,7 @@ ovsdb_atom_check_constraints(const union ovsdb_atom *atom,
                                DBL_DIG, atom->real,
                                DBL_DIG, base->u.real.max);
         }
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OVSDB_TYPE_BOOLEAN:
         return NULL;
@@ -849,7 +849,7 @@ ovsdb_atom_check_constraints(const union ovsdb_atom *atom,
 
     case OVSDB_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 \f
@@ -933,7 +933,7 @@ ovsdb_datum_default(const struct ovsdb_type *type)
         }
         return d;
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1119,7 +1119,7 @@ ovsdb_datum_sort_assert(struct ovsdb_datum *datum,
 {
     struct ovsdb_error *error = ovsdb_datum_sort(datum, key_type);
     if (error) {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
index a509c24..02bee75 100644 (file)
@@ -441,7 +441,7 @@ ovsdb_idl_get_mode(struct ovsdb_idl *idl,
         }
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 static void
@@ -505,7 +505,7 @@ ovsdb_idl_add_table(struct ovsdb_idl *idl,
         }
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 /* Turns off OVSDB_IDL_ALERT for 'column' in 'idl'.
index 83e6a56..8fe0d42 100644 (file)
@@ -147,10 +147,10 @@ ovsdb_base_type_init(struct ovsdb_base_type *base, enum ovsdb_atomic_type type)
         break;
 
     case OVSDB_N_TYPES:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -210,7 +210,7 @@ ovsdb_base_type_clone(struct ovsdb_base_type *dst,
 
     case OVSDB_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -239,10 +239,10 @@ ovsdb_base_type_destroy(struct ovsdb_base_type *base)
             break;
 
         case OVSDB_N_TYPES:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
 
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
 }
@@ -284,7 +284,7 @@ ovsdb_base_type_has_constraints(const struct ovsdb_base_type *base)
 
     switch (base->type) {
     case OVSDB_TYPE_VOID:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OVSDB_TYPE_INTEGER:
         return (base->u.integer.min != INT64_MIN
@@ -304,10 +304,10 @@ ovsdb_base_type_has_constraints(const struct ovsdb_base_type *base)
         return base->u.uuid.refTableName != NULL;
 
     case OVSDB_N_TYPES:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -481,7 +481,7 @@ ovsdb_base_type_to_json(const struct ovsdb_base_type *base)
 
     switch (base->type) {
     case OVSDB_TYPE_VOID:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case OVSDB_TYPE_INTEGER:
         if (base->u.integer.min != INT64_MIN) {
@@ -530,10 +530,10 @@ ovsdb_base_type_to_json(const struct ovsdb_base_type *base)
         break;
 
     case OVSDB_N_TYPES:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return json;
index d291c14..63873b0 100644 (file)
 struct ofpbuf;
 struct ds;
 
+/* Datapath packet metadata */
+struct pkt_metadata {
+    struct flow_tnl tunnel;     /* Encapsulating tunnel parameters. */
+    uint32_t skb_priority;      /* Packet priority for QoS. */
+    uint32_t pkt_mark;          /* Packet mark. */
+    odp_port_t in_port;         /* Input port. */
+};
+
+#define PKT_METADATA_INITIALIZER(PORT) \
+    (struct pkt_metadata){ { 0, 0, 0, 0, 0, 0}, 0, 0, (PORT) }
+
 bool dpid_from_string(const char *s, uint64_t *dpidp);
 
 #define ETH_ADDR_LEN           6
index ac68716..4e3e7db 100644 (file)
 #include <sys/stat.h>
 #include "byte-order.h"
 #include "compiler.h"
+#include "flow.h"
+#include "hmap.h"
 #include "ofpbuf.h"
+#include "packets.h"
+#include "timeval.h"
+#include "unaligned.h"
 #include "vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(pcap);
@@ -89,7 +94,7 @@ pcap_open(const char *file_name, const char *mode)
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
     return file;
 }
@@ -128,12 +133,13 @@ pcap_write_header(FILE *file)
 }
 
 int
-pcap_read(FILE *file, struct ofpbuf **bufp)
+pcap_read(FILE *file, struct ofpbuf **bufp, long long int *when)
 {
     struct pcaprec_hdr prh;
     struct ofpbuf *buf;
     void *data;
     size_t len;
+    bool swap;
 
     *bufp = NULL;
 
@@ -151,15 +157,22 @@ pcap_read(FILE *file, struct ofpbuf **bufp)
 
     /* Calculate length. */
     len = prh.incl_len;
-    if (len > 0xffff) {
-        uint32_t swapped_len = uint32_byteswap(len);
-        if (swapped_len > 0xffff) {
-            VLOG_WARN("bad packet length %"PRIuSIZE" or %"PRIu32" "
+    swap = len > 0xffff;
+    if (swap) {
+        len = uint32_byteswap(len);
+        if (len > 0xffff) {
+            VLOG_WARN("bad packet length %"PRIuSIZE" or %"PRIu32
                       "reading pcap file",
-                      len, swapped_len);
+                      len, uint32_byteswap(len));
             return EPROTO;
         }
-        len = swapped_len;
+    }
+
+    /* Calculate time. */
+    if (when) {
+        uint32_t ts_sec = swap ? uint32_byteswap(prh.ts_sec) : prh.ts_sec;
+        uint32_t ts_usec = swap ? uint32_byteswap(prh.ts_usec) : prh.ts_usec;
+        *when = ts_sec * 1000LL + ts_usec / 1000;
     }
 
     /* Read packet. */
@@ -180,10 +193,147 @@ void
 pcap_write(FILE *file, struct ofpbuf *buf)
 {
     struct pcaprec_hdr prh;
-    prh.ts_sec = 0;
-    prh.ts_usec = 0;
+    struct timeval tv;
+
+    xgettimeofday(&tv);
+    prh.ts_sec = tv.tv_sec;
+    prh.ts_usec = tv.tv_usec;
     prh.incl_len = buf->size;
     prh.orig_len = buf->size;
     ignore(fwrite(&prh, sizeof prh, 1, file));
     ignore(fwrite(buf->data, buf->size, 1, file));
 }
+\f
+struct tcp_key {
+    ovs_be32 nw_src, nw_dst;
+    ovs_be16 tp_src, tp_dst;
+};
+
+struct tcp_stream {
+    struct hmap_node hmap_node;
+    struct tcp_key key;
+    uint32_t seq_no;
+    struct ofpbuf payload;
+};
+
+struct tcp_reader {
+    struct hmap streams;
+};
+
+static void
+tcp_stream_destroy(struct tcp_reader *r, struct tcp_stream *stream)
+{
+    hmap_remove(&r->streams, &stream->hmap_node);
+    ofpbuf_uninit(&stream->payload);
+    free(stream);
+}
+
+/* Returns a new data structure for extracting TCP stream data from an
+ * Ethernet packet capture */
+struct tcp_reader *
+tcp_reader_open(void)
+{
+    struct tcp_reader *r;
+
+    r = xmalloc(sizeof *r);
+    hmap_init(&r->streams);
+    return r;
+}
+
+/* Closes and frees 'r'. */
+void
+tcp_reader_close(struct tcp_reader *r)
+{
+    struct tcp_stream *stream, *next_stream;
+
+    HMAP_FOR_EACH_SAFE (stream, next_stream, hmap_node, &r->streams) {
+        tcp_stream_destroy(r, stream);
+    }
+    hmap_destroy(&r->streams);
+    free(r);
+}
+
+static struct tcp_stream *
+tcp_stream_lookup(struct tcp_reader *r, const struct flow *flow)
+{
+    struct tcp_stream *stream;
+    struct tcp_key key;
+    uint32_t hash;
+
+    memset(&key, 0, sizeof key);
+    key.nw_src = flow->nw_src;
+    key.nw_dst = flow->nw_dst;
+    key.tp_src = flow->tp_src;
+    key.tp_dst = flow->tp_dst;
+    hash = hash_bytes(&key, sizeof key, 0);
+
+    HMAP_FOR_EACH_WITH_HASH (stream, hmap_node, hash, &r->streams) {
+        if (!memcmp(&stream->key, &key, sizeof key)) {
+            return stream;
+        }
+    }
+
+    stream = xmalloc(sizeof *stream);
+    hmap_insert(&r->streams, &stream->hmap_node, hash);
+    memcpy(&stream->key, &key, sizeof key);
+    stream->seq_no = 0;
+    ofpbuf_init(&stream->payload, 2048);
+    return stream;
+}
+
+/* Processes 'packet' through TCP reader 'r'.  The caller must have already
+ * extracted the packet's headers into 'flow', using flow_extract().
+ *
+ * If 'packet' is a TCP packet, then the reader attempts to reconstruct the
+ * data stream.  If successful, it returns an ofpbuf that represents the data
+ * stream so far.  The caller may examine the data in the ofpbuf and pull off
+ * any data that it has fully processed.  The remaining data that the caller
+ * does not pull off will be presented again in future calls if more data
+ * arrives in the stream.
+ *
+ * Returns null if 'packet' doesn't add new data to a TCP stream. */
+struct ofpbuf *
+tcp_reader_run(struct tcp_reader *r, const struct flow *flow,
+               const struct ofpbuf *packet)
+{
+    struct tcp_stream *stream;
+    struct tcp_header *tcp;
+    struct ofpbuf *payload;
+    uint32_t seq;
+    uint8_t flags;
+
+    if (flow->dl_type != htons(ETH_TYPE_IP)
+        || flow->nw_proto != IPPROTO_TCP
+        || !packet->l7) {
+        return NULL;
+    }
+
+    stream = tcp_stream_lookup(r, flow);
+    payload = &stream->payload;
+
+    tcp = packet->l4;
+    flags = TCP_FLAGS(tcp->tcp_ctl);
+    seq = ntohl(get_16aligned_be32(&tcp->tcp_seq));
+    if (flags & TCP_SYN) {
+        ofpbuf_clear(payload);
+        stream->seq_no = seq + 1;
+        return NULL;
+    } else if (flags & (TCP_FIN | TCP_RST)) {
+        tcp_stream_destroy(r, stream);
+        return NULL;
+    } else if (seq == stream->seq_no) {
+        size_t length;
+
+        /* Shift all of the existing payload to the very beginning of the
+         * allocated space, so that we reuse allocated space instead of
+         * continually expanding it. */
+        ofpbuf_shift(payload, (char *) payload->base - (char *) payload->data);
+
+        length = (char *) ofpbuf_end(packet) - (char *) packet->l7;
+        ofpbuf_put(payload, packet->l7, length);
+        stream->seq_no += length;
+        return payload;
+    } else {
+        return NULL;
+    }
+}
index 46625c3..ef491e5 100644 (file)
 
 #include <stdio.h>
 
+struct flow;
 struct ofpbuf;
 
+/* PCAP file reading and writing. */
 FILE *pcap_open(const char *file_name, const char *mode);
 int pcap_read_header(FILE *);
 void pcap_write_header(FILE *);
-int pcap_read(FILE *, struct ofpbuf **);
+int pcap_read(FILE *, struct ofpbuf **, long long int *when);
 void pcap_write(FILE *, struct ofpbuf *);
+\f
+/* Extracting TCP stream data from an Ethernet packet capture. */
+
+struct tcp_reader *tcp_reader_open(void);
+void tcp_reader_close(struct tcp_reader *);
+struct ofpbuf *tcp_reader_run(struct tcp_reader *, const struct flow *,
+                              const struct ofpbuf *);
 
 #endif /* pcap-file.h */
index d6f7d9d..c0bc659 100644 (file)
@@ -86,17 +86,6 @@ random_bytes(void *p_, size_t n)
     }
 }
 
-uint8_t
-random_uint8(void)
-{
-    return random_uint32();
-}
-
-uint16_t
-random_uint16(void)
-{
-    return random_uint32();
-}
 
 uint32_t
 random_uint32(void)
@@ -117,12 +106,6 @@ random_uint64(void)
     return x;
 }
 
-int
-random_range(int max)
-{
-    return random_uint32() % max;
-}
-
 static uint32_t
 random_next(void)
 {
index eaac143..5fe50b7 100644 (file)
@@ -24,10 +24,25 @@ void random_init(void);
 void random_set_seed(uint32_t);
 
 void random_bytes(void *, size_t);
-uint8_t random_uint8(void);
-uint16_t random_uint16(void);
 uint32_t random_uint32(void);
 uint64_t random_uint64(void);
-int random_range(int max);
+
+static inline int
+random_range(int max)
+{
+    return random_uint32() % max;
+}
+
+static inline uint8_t
+random_uint8(void)
+{
+    return random_uint32();
+}
+
+static inline uint16_t
+random_uint16(void)
+{
+    return random_uint32();
+}
 
 #endif /* random.h */
index f7f90f7..d339365 100644 (file)
@@ -115,17 +115,6 @@ struct rconn {
     int probe_interval;         /* Secs of inactivity before sending probe. */
     time_t last_activity;       /* Last time we saw some activity. */
 
-    /* When we create a vconn we obtain these values, to save them past the end
-     * of the vconn's lifetime.  Otherwise, in-band control will only allow
-     * traffic when a vconn is actually open, but it is nice to allow ARP to
-     * complete even between connection attempts, and it is also polite to
-     * allow traffic from other switches to go through to the controller
-     * whether or not we are connected.
-     *
-     * We don't cache the local port, because that changes from one connection
-     * attempt to the next. */
-    ovs_be32 local_ip, remote_ip;
-    ovs_be16 remote_port;
     uint8_t dscp;
 
     /* Messages sent or received are copied to the monitor connections. */
@@ -456,9 +445,6 @@ reconnect(struct rconn *rc)
     retval = vconn_open(rc->target, rc->allowed_versions, rc->dscp,
                         &rc->vconn);
     if (!retval) {
-        rc->remote_ip = vconn_get_remote_ip(rc->vconn);
-        rc->local_ip = vconn_get_local_ip(rc->vconn);
-        rc->remote_port = vconn_get_remote_port(rc->vconn);
         rc->backoff_deadline = time_now() + rc->backoff;
         state_transition(rc, S_CONNECTING);
     } else {
@@ -632,7 +618,7 @@ rconn_run(struct rconn *rc)
             STATES
 #undef STATE
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     } while (rc->state != old_state);
     ovs_mutex_unlock(&rc->mutex);
@@ -911,46 +897,6 @@ rconn_failure_duration(const struct rconn *rconn)
     return duration;
 }
 
-/* Returns the IP address of the peer, or 0 if the peer's IP address is not
- * known. */
-ovs_be32
-rconn_get_remote_ip(const struct rconn *rconn)
-{
-    return rconn->remote_ip;
-}
-
-/* Returns the transport port of the peer, or 0 if the peer's port is not
- * known. */
-ovs_be16
-rconn_get_remote_port(const struct rconn *rconn)
-{
-    return rconn->remote_port;
-}
-
-/* Returns the IP address used to connect to the peer, or 0 if the
- * connection is not an IP-based protocol or if its IP address is not
- * known. */
-ovs_be32
-rconn_get_local_ip(const struct rconn *rconn)
-{
-    return rconn->local_ip;
-}
-
-/* Returns the transport port used to connect to the peer, or 0 if the
- * connection does not contain a port or if the port is not known. */
-ovs_be16
-rconn_get_local_port(const struct rconn *rconn)
-    OVS_EXCLUDED(rconn->mutex)
-{
-    ovs_be16 port;
-
-    ovs_mutex_lock(&rconn->mutex);
-    port = rconn->vconn ? vconn_get_local_port(rconn->vconn) : 0;
-    ovs_mutex_unlock(&rconn->mutex);
-
-    return port;
-}
-
 static int
 rconn_get_version__(const struct rconn *rconn)
     OVS_REQUIRES(rconn->mutex)
@@ -1139,9 +1085,6 @@ rconn_set_target__(struct rconn *rc, const char *target, const char *name)
     rc->name = xstrdup(name ? name : target);
     free(rc->target);
     rc->target = xstrdup(target);
-    rc->local_ip = 0;
-    rc->remote_ip = 0;
-    rc->remote_port = 0;
 }
 
 /* Tries to send a packet from 'rc''s send buffer.  Returns 0 if successful,
@@ -1280,7 +1223,7 @@ timeout(const struct rconn *rc)
         STATES
 #undef STATE
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1349,6 +1292,17 @@ is_connected_state(enum state state)
     return (state & (S_ACTIVE | S_IDLE)) != 0;
 }
 
+/* When a switch initially connects to a controller, the controller may spend a
+ * little time examining the switch, looking at, for example, its datapath ID,
+ * before it decides whether it is willing to control that switch.  At that
+ * point, it either disconnects or starts controlling the switch.
+ *
+ * This function returns a guess to its caller about whether 'b' is OpenFlow
+ * message that indicates that the controller has decided to control the
+ * switch.  It returns false if the message is one that a controller typically
+ * uses to determine whether a switch is admissible, true if the message is one
+ * that would typically be used only after the controller has admitted the
+ * switch. */
 static bool
 is_admitted_msg(const struct ofpbuf *b)
 {
@@ -1370,7 +1324,6 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_GET_CONFIG_REQUEST:
     case OFPTYPE_GET_CONFIG_REPLY:
     case OFPTYPE_SET_CONFIG:
-        /* FIXME: Change the following once they are implemented: */
     case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
     case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
     case OFPTYPE_GET_ASYNC_REQUEST:
index 408cec9..2cb43f7 100644 (file)
@@ -83,10 +83,6 @@ bool rconn_is_connected(const struct rconn *);
 bool rconn_is_admitted(const struct rconn *);
 int rconn_failure_duration(const struct rconn *);
 
-ovs_be32 rconn_get_remote_ip(const struct rconn *);
-ovs_be16 rconn_get_remote_port(const struct rconn *);
-ovs_be32 rconn_get_local_ip(const struct rconn *);
-ovs_be16 rconn_get_local_port(const struct rconn *);
 int rconn_get_version(const struct rconn *);
 
 const char *rconn_get_state(const struct rconn *);
index fae35b1..0a773bc 100644 (file)
@@ -532,7 +532,7 @@ reconnect_deadline__(const struct reconnect *fsm)
         return fsm->state_entered;
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 /* Assesses whether any action should be taken on 'fsm'.  The return value is
@@ -604,7 +604,7 @@ reconnect_run(struct reconnect *fsm, long long int now)
             return 0;
         }
 
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     } else {
         return 0;
     }
diff --git a/lib/stdio.c b/lib/stdio.c
new file mode 100644 (file)
index 0000000..49a5078
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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 <stdio.h>
+
+#ifdef _WIN32
+#undef snprintf
+#undef vsnprintf
+
+int
+ovs_snprintf(char *s, size_t n, const char *format, ... )
+{
+    va_list args;
+    int len;
+
+    va_start(args, format);
+    len = ovs_vsnprintf(s, n, format, args);
+    va_end(args);
+
+    return len;
+}
+
+int
+ovs_vsnprintf(char *s, size_t n, const char *format, va_list args)
+{
+    int needed = _vscprintf(format, args);
+    if (s && n) {
+        vsnprintf(s, n, format, args);
+        s[n - 1] = '\0';
+    }
+    return needed;
+}
+#endif  /* _WIN32 */
diff --git a/lib/stdio.h.in b/lib/stdio.h.in
new file mode 100644 (file)
index 0000000..3bf1f03
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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_NEXT@ @NEXT_STDIO_H@
+
+#if !defined STDIO_H_WRAPPER
+#define STDIO_H_WRAPPER 1
+
+#ifdef _WIN32
+
+#include <stdarg.h>
+#include <stddef.h>
+
+/* Windows libc has defective snprintf() and vsnprintf():
+ *
+ *     - They return -1 if the output won't fit.
+ *
+ *     - They don't null-terminate the output if it won't fit.
+ *
+ * We need working versions so here we define substitutes. */
+#undef snprintf
+#define snprintf ovs_snprintf
+int ovs_snprintf(char *, size_t, const char *, ...);
+
+#undef vsnprintf
+#define vsnprintf ovs_vsnprintf
+int ovs_vsnprintf(char *, size_t, const char *, va_list);
+#endif /* _WIN32 */
+
+#endif /* stdio.h wrapper */
index 725bfff..8140263 100644 (file)
--- a/lib/stp.c
+++ b/lib/stp.c
 #include <inttypes.h>
 #include <stdlib.h>
 #include "byte-order.h"
+#include "connectivity.h"
 #include "ofpbuf.h"
 #include "packets.h"
+#include "seq.h"
 #include "unixctl.h"
 #include "util.h"
 #include "vlog.h"
@@ -666,7 +668,7 @@ stp_state_name(enum stp_state state)
     case STP_BLOCKING:
         return "blocking";
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -707,7 +709,7 @@ stp_role_name(enum stp_role role)
     case STP_ROLE_DISABLED:
         return "disabled";
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1127,6 +1129,7 @@ stp_configuration_update(struct stp *stp) OVS_REQUIRES(mutex)
 {
     stp_root_selection(stp);
     stp_designated_port_selection(stp);
+    seq_change(connectivity_seq_get());
 }
 
 static bool
@@ -1257,6 +1260,7 @@ stp_set_port_state(struct stp_port *p, enum stp_state state)
         if (p < p->stp->first_changed_port) {
             p->stp->first_changed_port = p;
         }
+        seq_change(connectivity_seq_get());
     }
     p->state = state;
 }
@@ -1275,6 +1279,7 @@ stp_topology_change_detection(struct stp *stp) OVS_REQUIRES(mutex)
     }
     stp->fdb_needs_flush = true;
     stp->topology_change_detected = true;
+    seq_change(connectivity_seq_get());
     VLOG_INFO_RL(&rl, "%s: detected topology change.", stp->name);
 }
 
index 1171f32..81c5a43 100644 (file)
@@ -125,7 +125,7 @@ fd_wait(struct stream *stream, enum stream_wait_type wait)
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
index 43c63e8..44d75d3 100644 (file)
@@ -29,19 +29,11 @@ struct stream {
     const struct stream_class *class;
     int state;
     int error;
-    ovs_be32 remote_ip;
-    ovs_be16 remote_port;
-    ovs_be32 local_ip;
-    ovs_be16 local_port;
     char *name;
 };
 
 void stream_init(struct stream *, const struct stream_class *,
                  int connect_status, const char *name);
-void stream_set_remote_ip(struct stream *, ovs_be32 remote_ip);
-void stream_set_remote_port(struct stream *, ovs_be16 remote_port);
-void stream_set_local_ip(struct stream *, ovs_be32 local_ip);
-void stream_set_local_port(struct stream *, ovs_be16 local_port);
 static inline void stream_assert_class(const struct stream *stream,
                                        const struct stream_class *class)
 {
index f2bd513..2ed5282 100644 (file)
@@ -189,7 +189,7 @@ want_to_poll_events(int want)
 {
     switch (want) {
     case SSL_NOTHING:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case SSL_READING:
         return POLLIN;
@@ -198,14 +198,13 @@ want_to_poll_events(int want)
         return POLLOUT;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
 static int
 new_ssl_stream(const char *name, int fd, enum session_type type,
-               enum ssl_state state, const struct sockaddr_in *remote,
-               struct stream **streamp)
+               enum ssl_state state, struct stream **streamp)
 {
     struct sockaddr_in local;
     socklen_t local_len = sizeof local;
@@ -270,10 +269,6 @@ new_ssl_stream(const char *name, int fd, enum session_type type,
     /* Create and return the ssl_stream. */
     sslv = xmalloc(sizeof *sslv);
     stream_init(&sslv->stream, &ssl_stream_class, EAGAIN, name);
-    stream_set_remote_ip(&sslv->stream, remote->sin_addr.s_addr);
-    stream_set_remote_port(&sslv->stream, remote->sin_port);
-    stream_set_local_ip(&sslv->stream, local.sin_addr.s_addr);
-    stream_set_local_port(&sslv->stream, local.sin_port);
     sslv->state = state;
     sslv->type = type;
     sslv->fd = fd;
@@ -309,7 +304,6 @@ ssl_stream_cast(struct stream *stream)
 static int
 ssl_open(const char *name, char *suffix, struct stream **streamp, uint8_t dscp)
 {
-    struct sockaddr_in sin;
     int error, fd;
 
     error = ssl_init();
@@ -317,11 +311,11 @@ ssl_open(const char *name, char *suffix, struct stream **streamp, uint8_t dscp)
         return error;
     }
 
-    error = inet_open_active(SOCK_STREAM, suffix, OFP_OLD_PORT, &sin, &fd,
+    error = inet_open_active(SOCK_STREAM, suffix, OFP_OLD_PORT, NULL, &fd,
                              dscp);
     if (fd >= 0) {
         int state = error ? STATE_TCP_CONNECTING : STATE_SSL_CONNECTING;
-        return new_ssl_stream(name, fd, CLIENT, state, &sin, streamp);
+        return new_ssl_stream(name, fd, CLIENT, state, streamp);
     } else {
         VLOG_ERR("%s: connect: %s", name, ovs_strerror(error));
         return error;
@@ -484,7 +478,7 @@ ssl_connect(struct stream *stream)
         }
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 static void
@@ -724,7 +718,7 @@ ssl_wait(struct stream *stream, enum stream_wait_type wait)
                 break;
 
             default:
-                NOT_REACHED();
+                OVS_NOT_REACHED();
             }
         }
         break;
@@ -748,7 +742,7 @@ ssl_wait(struct stream *stream, enum stream_wait_type wait)
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -849,7 +843,7 @@ pssl_accept(struct pstream *pstream, struct stream **new_streamp)
     if (sin.sin_port != htons(OFP_OLD_PORT)) {
         sprintf(strchr(name, '\0'), ":%"PRIu16, ntohs(sin.sin_port));
     }
-    return new_ssl_stream(name, new_fd, SERVER, STATE_SSL_CONNECTING, &sin,
+    return new_ssl_stream(name, new_fd, SERVER, STATE_SSL_CONNECTING,
                           new_streamp);
 }
 
index a4cdf45..b3237d6 100644 (file)
@@ -38,7 +38,7 @@ VLOG_DEFINE_THIS_MODULE(stream_tcp);
 
 static int
 new_tcp_stream(const char *name, int fd, int connect_status,
-               const struct sockaddr_in *remote, struct stream **streamp)
+               struct stream **streamp)
 {
     struct sockaddr_in local;
     socklen_t local_len = sizeof local;
@@ -58,26 +58,17 @@ new_tcp_stream(const char *name, int fd, int connect_status,
         return errno;
     }
 
-    retval = new_fd_stream(name, fd, connect_status, streamp);
-    if (!retval) {
-        struct stream *stream = *streamp;
-        stream_set_remote_ip(stream, remote->sin_addr.s_addr);
-        stream_set_remote_port(stream, remote->sin_port);
-        stream_set_local_ip(stream, local.sin_addr.s_addr);
-        stream_set_local_port(stream, local.sin_port);
-    }
-    return retval;
+    return new_fd_stream(name, fd, connect_status, streamp);
 }
 
 static int
 tcp_open(const char *name, char *suffix, struct stream **streamp, uint8_t dscp)
 {
-    struct sockaddr_in sin;
     int fd, error;
 
-    error = inet_open_active(SOCK_STREAM, suffix, 0, &sin, &fd, dscp);
+    error = inet_open_active(SOCK_STREAM, suffix, 0, NULL, &fd, dscp);
     if (fd >= 0) {
-        return new_tcp_stream(name, fd, error, &sin, streamp);
+        return new_tcp_stream(name, fd, error, streamp);
     } else {
         VLOG_ERR("%s: connect: %s", name, ovs_strerror(error));
         return error;
@@ -140,7 +131,7 @@ ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len,
     } else {
         strcpy(name, "tcp");
     }
-    return new_tcp_stream(name, fd, 0, sin, streamp);
+    return new_tcp_stream(name, fd, 0, streamp);
 }
 
 const struct pstream_class ptcp_pstream_class = {
index 0442d84..3542455 100644 (file)
@@ -281,38 +281,6 @@ stream_get_name(const struct stream *stream)
     return stream ? stream->name : "(null)";
 }
 
-/* Returns the IP address of the peer, or 0 if the peer is not connected over
- * an IP-based protocol or if its IP address is not yet known. */
-ovs_be32
-stream_get_remote_ip(const struct stream *stream)
-{
-    return stream->remote_ip;
-}
-
-/* Returns the transport port of the peer, or 0 if the connection does not
- * contain a port or if the port is not yet known. */
-ovs_be16
-stream_get_remote_port(const struct stream *stream)
-{
-    return stream->remote_port;
-}
-
-/* Returns the IP address used to connect to the peer, or 0 if the connection
- * is not an IP-based protocol or if its IP address is not yet known. */
-ovs_be32
-stream_get_local_ip(const struct stream *stream)
-{
-    return stream->local_ip;
-}
-
-/* Returns the transport port used to connect to the peer, or 0 if the
- * connection does not contain a port or if the port is not yet known. */
-ovs_be16
-stream_get_local_port(const struct stream *stream)
-{
-    return stream->local_port;
-}
-
 static void
 scs_connecting(struct stream *stream)
 {
@@ -349,7 +317,7 @@ stream_connect(struct stream *stream)
             return stream->error;
 
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     } while (stream->state != last_state);
 
@@ -662,30 +630,6 @@ stream_init(struct stream *stream, const struct stream_class *class,
     ovs_assert(stream->state != SCS_CONNECTING || class->connect);
 }
 
-void
-stream_set_remote_ip(struct stream *stream, ovs_be32 ip)
-{
-    stream->remote_ip = ip;
-}
-
-void
-stream_set_remote_port(struct stream *stream, ovs_be16 port)
-{
-    stream->remote_port = port;
-}
-
-void
-stream_set_local_ip(struct stream *stream, ovs_be32 ip)
-{
-    stream->local_ip = ip;
-}
-
-void
-stream_set_local_port(struct stream *stream, ovs_be16 port)
-{
-    stream->local_port = port;
-}
-
 void
 pstream_init(struct pstream *pstream, const struct pstream_class *class,
             const char *name)
similarity index 86%
rename from lib/string.h
rename to lib/string.h.in
index 2b7b454..9e8fba9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2011 Nicira, Inc.
+ * Copyright (c) 2009, 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.
@@ -17,7 +17,7 @@
 #ifndef STRING_WRAPPER_H
 #define STRING_WRAPPER_H 1
 
-#include_next <string.h>
+#@INCLUDE_NEXT@ @NEXT_STRING_H@
 
 /* Glibc 2.7 has a bug in strtok_r when compiling with optimization that can
  * cause segfaults if the delimiters argument is a compile-time constant that
 #undef strtok_r
 #endif
 
+#ifdef _WIN32
+#define strtok_r strtok_s
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#endif
+
 #ifndef HAVE_STRNLEN
 #undef strnlen
 #define strnlen rpl_strnlen
index 2ce45fc..5d01708 100644 (file)
@@ -535,6 +535,7 @@ timeval_warp_cb(struct unixctl_conn *conn,
     timespec_add(&monotonic_clock.warp, &monotonic_clock.warp, &ts);
     ovs_mutex_unlock(&monotonic_clock.mutex);
     seq_change(timewarp_seq);
+    poll(NULL, 0, 10); /* give threads (eg. monitor) some chances to run */
     unixctl_command_reply(conn, "warped");
 }
 
index 13d41a7..984ab45 100644 (file)
@@ -67,7 +67,7 @@ ovs_assert_failure(const char *where, const char *function,
     case 0:
         VLOG_ABORT("%s: assertion %s failed in %s()",
                    where, condition, function);
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case 1:
         fprintf(stderr, "%s: assertion %s failed in %s()",
@@ -537,24 +537,6 @@ str_to_llong(const char *s, int base, long long *x)
     }
 }
 
-bool
-str_to_uint(const char *s, int base, unsigned int *u)
-{
-    return str_to_int(s, base, (int *) u);
-}
-
-bool
-str_to_ulong(const char *s, int base, unsigned long *ul)
-{
-    return str_to_long(s, base, (long *) ul);
-}
-
-bool
-str_to_ullong(const char *s, int base, unsigned long long *ull)
-{
-    return str_to_llong(s, base, (long long *) ull);
-}
-
 /* Converts floating-point string 's' into a double.  If successful, stores
  * the double in '*d' and returns true; on failure, stores 0 in '*d' and
  * returns false.
@@ -901,7 +883,7 @@ raw_clz64(uint64_t n)
 }
 #endif
 
-#if !(__GNUC__ >= 4 && defined(__corei7))
+#if NEED_COUNT_1BITS_8
 #define INIT1(X)                                \
     ((((X) & (1 << 0)) != 0) +                  \
      (((X) & (1 << 1)) != 0) +                  \
@@ -1378,7 +1360,7 @@ scan_float(const char *s, const struct scan_spec *spec, va_list *args)
     case SCAN_INTMAX_T:
     case SCAN_PTRDIFF_T:
     case SCAN_SIZE_T:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
     return s;
 }
@@ -1483,7 +1465,7 @@ scan_chars(const char *s, const struct scan_spec *spec, va_list *args)
 /* This is an implementation of the standard sscanf() function, with the
  * following exceptions:
  *
- *   - It returns true if the entire template was successfully scanned and
+ *   - It returns true if the entire format was successfully scanned and
  *     converted, false if any conversion failed.
  *
  *   - The standard doesn't define sscanf() behavior when an out-of-range value
@@ -1500,15 +1482,15 @@ scan_chars(const char *s, const struct scan_spec *spec, va_list *args)
  *   - %p is not supported.
  */
 bool
-ovs_scan(const char *s, const char *template, ...)
+ovs_scan(const char *s, const char *format, ...)
 {
     const char *const start = s;
     bool ok = false;
     const char *p;
     va_list args;
 
-    va_start(args, template);
-    p = template;
+    va_start(args, format);
+    p = format;
     while (*p != '\0') {
         struct scan_spec spec;
         unsigned char c = *p++;
index 7c5eacb..8886a54 100644 (file)
@@ -150,7 +150,7 @@ is_pow2(uintmax_t x)
 #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
 #endif
 
-#define NOT_REACHED() abort()
+#define OVS_NOT_REACHED() abort()
 
 /* Expands to a string that looks like "<file>:<line>", e.g. "tmp.c:10".
  *
@@ -277,11 +277,26 @@ void ovs_hex_dump(FILE *, const void *, size_t, uintptr_t offset, bool ascii);
 bool str_to_int(const char *, int base, int *);
 bool str_to_long(const char *, int base, long *);
 bool str_to_llong(const char *, int base, long long *);
-bool str_to_uint(const char *, int base, unsigned int *);
-bool str_to_ulong(const char *, int base, unsigned long *);
-bool str_to_ullong(const char *, int base, unsigned long long *);
 
-bool ovs_scan(const char *s, const char *template, ...) SCANF_FORMAT(2, 3);
+static inline bool
+str_to_uint(const char *s, int base, unsigned int *u)
+{
+    return str_to_int(s, base, (int *) u);
+}
+
+static inline bool
+str_to_ulong(const char *s, int base, unsigned long *ul)
+{
+    return str_to_long(s, base, (long *) ul);
+}
+
+static inline bool
+str_to_ullong(const char *s, int base, unsigned long long *ull)
+{
+    return str_to_llong(s, base, (long long *) ull);
+}
+
+bool ovs_scan(const char *s, const char *format, ...) SCANF_FORMAT(2, 3);
 
 bool str_to_double(const char *, double *);
 
@@ -371,48 +386,55 @@ log_2_ceil(uint64_t n)
     return log_2_floor(n) + !is_pow2(n);
 }
 
-/* Returns the number of 1-bits in 'x', between 0 and 32 inclusive. */
+/* unsigned int count_1bits(uint64_t x):
+ *
+ * Returns the number of 1-bits in 'x', between 0 and 64 inclusive. */
+#if UINTPTR_MAX == UINT64_MAX
 static inline unsigned int
-count_1bits_32(uint32_t x)
+count_1bits(uint64_t x)
+{
+#if __GNUC__ >= 4 && __POPCNT__
+    return __builtin_popcountll(x);
+#else
+    /* This portable implementation is the fastest one we know of for 64
+     * bits, and about 3x faster than GCC 4.7 __builtin_popcountll(). */
+    const uint64_t h55 = UINT64_C(0x5555555555555555);
+    const uint64_t h33 = UINT64_C(0x3333333333333333);
+    const uint64_t h0F = UINT64_C(0x0F0F0F0F0F0F0F0F);
+    const uint64_t h01 = UINT64_C(0x0101010101010101);
+    x -= (x >> 1) & h55;               /* Count of each 2 bits in-place. */
+    x = (x & h33) + ((x >> 2) & h33);  /* Count of each 4 bits in-place. */
+    x = (x + (x >> 4)) & h0F;          /* Count of each 8 bits in-place. */
+    return (x * h01) >> 56;            /* Sum of all bytes. */
+#endif
+}
+#else /* Not 64-bit. */
+#if __GNUC__ >= 4 && __POPCNT__
+static inline unsigned int
+count_1bits_32__(uint32_t x)
 {
-#if __GNUC__ >= 4 && defined(__corei7)
-    /* __builtin_popcount() is fast only when supported by the CPU. */
     return __builtin_popcount(x);
+}
 #else
-    extern const uint8_t count_1bits_8[256];
+#define NEED_COUNT_1BITS_8 1
+extern const uint8_t count_1bits_8[256];
+static inline unsigned int
+count_1bits_32__(uint32_t x)
+{
     /* This portable implementation is the fastest one we know of for 32 bits,
      * and faster than GCC __builtin_popcount(). */
     return (count_1bits_8[x & 0xff] +
             count_1bits_8[(x >> 8) & 0xff] +
             count_1bits_8[(x >> 16) & 0xff] +
             count_1bits_8[x >> 24]);
-#endif
 }
-
-/* Returns the number of 1-bits in 'x', between 0 and 64 inclusive. */
+#endif
 static inline unsigned int
 count_1bits(uint64_t x)
 {
-    if (sizeof(void *) == 8) { /* 64-bit CPU */
-#if __GNUC__ >= 4 && defined(__corei7)
-        /* __builtin_popcountll() is fast only when supported by the CPU. */
-        return __builtin_popcountll(x);
-#else
-        /* This portable implementation is the fastest one we know of for 64
-         * bits, and about 3x faster than GCC 4.7 __builtin_popcountll(). */
-        const uint64_t h55 = UINT64_C(0x5555555555555555);
-        const uint64_t h33 = UINT64_C(0x3333333333333333);
-        const uint64_t h0F = UINT64_C(0x0F0F0F0F0F0F0F0F);
-        const uint64_t h01 = UINT64_C(0x0101010101010101);
-        x -= (x >> 1) & h55;               /* Count of each 2 bits in-place. */
-        x = (x & h33) + ((x >> 2) & h33);  /* Count of each 4 bits in-place. */
-        x = (x + (x >> 4)) & h0F;          /* Count of each 8 bits in-place. */
-        return (x * h01) >> 56;            /* Sum of all bytes. */
-#endif
-    } else { /* 32-bit CPU */
-        return count_1bits_32(x) + count_1bits_32(x >> 32);
-    }
+    return count_1bits_32__(x) + count_1bits_32__(x >> 32);
 }
+#endif
 
 /* Returns the rightmost 1-bit in 'x' (e.g. 01011000 => 00001000), or 0 if 'x'
  * is 0. */
index 640c5b6..b05bb45 100644 (file)
@@ -40,21 +40,12 @@ struct vconn {
     enum ofp_version version;   /* Negotiated version (or 0). */
     bool recv_any_version;      /* True to receive a message of any version. */
 
-    ovs_be32 remote_ip;
-    ovs_be16 remote_port;
-    ovs_be32 local_ip;
-    ovs_be16 local_port;
-
     char *name;
 };
 
 void vconn_init(struct vconn *, const struct vconn_class *, int connect_status,
                 const char *name, uint32_t allowed_versions);
 void vconn_free_data(struct vconn *vconn);
-void vconn_set_remote_ip(struct vconn *, ovs_be32 remote_ip);
-void vconn_set_remote_port(struct vconn *, ovs_be16 remote_port);
-void vconn_set_local_ip(struct vconn *, ovs_be32 local_ip);
-void vconn_set_local_port(struct vconn *, ovs_be16 local_port);
 static inline void vconn_assert_class(const struct vconn *vconn,
                                       const struct vconn_class *class)
 {
index 027f48f..d169840 100644 (file)
@@ -64,10 +64,6 @@ vconn_stream_new(struct stream *stream, int connect_status,
     s->txbuf = NULL;
     s->rxbuf = NULL;
     s->n_packets = 0;
-    s->vconn.remote_ip = stream_get_remote_ip(stream);
-    s->vconn.remote_port = stream_get_remote_port(stream);
-    s->vconn.local_ip = stream_get_local_ip(stream);
-    s->vconn.local_port = stream_get_local_port(stream);
     return &s->vconn;
 }
 
@@ -281,7 +277,7 @@ vconn_stream_wait(struct vconn *vconn, enum vconn_wait_type wait)
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 \f
index 5708987..f0549d5 100644 (file)
@@ -350,39 +350,6 @@ vconn_set_allowed_versions(struct vconn *vconn, uint32_t allowed_versions)
     vconn->allowed_versions = allowed_versions;
 }
 
-/* Returns the IP address of the peer, or 0 if the peer is not connected over
- * an IP-based protocol or if its IP address is not yet known. */
-ovs_be32
-vconn_get_remote_ip(const struct vconn *vconn)
-{
-    return vconn->remote_ip;
-}
-
-/* Returns the transport port of the peer, or 0 if the connection does not
- * contain a port or if the port is not yet known. */
-ovs_be16
-vconn_get_remote_port(const struct vconn *vconn)
-{
-    return vconn->remote_port;
-}
-
-/* Returns the IP address used to connect to the peer, or 0 if the
- * connection is not an IP-based protocol or if its IP address is not
- * yet known. */
-ovs_be32
-vconn_get_local_ip(const struct vconn *vconn)
-{
-    return vconn->local_ip;
-}
-
-/* Returns the transport port used to connect to the peer, or 0 if the
- * connection does not contain a port or if the port is not yet known. */
-ovs_be16
-vconn_get_local_port(const struct vconn *vconn)
-{
-    return vconn->local_port;
-}
-
 /* Returns the OpenFlow version negotiated with the peer, or -1 if version
  * negotiation is not yet complete.
  *
@@ -596,7 +563,7 @@ vconn_connect(struct vconn *vconn)
             return vconn->error;
 
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     } while (vconn->state != last_state);
 
@@ -631,11 +598,25 @@ vconn_recv(struct vconn *vconn, struct ofpbuf **msgp)
                     type != OFPTYPE_ERROR &&
                     type != OFPTYPE_ECHO_REQUEST &&
                     type != OFPTYPE_ECHO_REPLY)) {
+                struct ofpbuf *reply;
+
                 VLOG_ERR_RL(&bad_ofmsg_rl, "%s: received OpenFlow version "
                             "0x%02"PRIx8" != expected %02x",
                             vconn->name, oh->version, vconn->version);
+
+                /* Send a "bad version" reply, if we can. */
+                reply = ofperr_encode_reply(OFPERR_OFPBRC_BAD_VERSION, oh);
+                retval = vconn_send(vconn, reply);
+                if (retval) {
+                    VLOG_INFO_RL(&bad_ofmsg_rl,
+                                 "%s: failed to queue error reply (%s)",
+                                 vconn->name, ovs_strerror(retval));
+                    ofpbuf_delete(reply);
+                }
+
+                /* Suppress the received message, as if it had not arrived. */
+                retval = EAGAIN;
                 ofpbuf_delete(msg);
-                retval = EPROTO;
             }
         }
     }
@@ -1123,30 +1104,6 @@ vconn_init(struct vconn *vconn, const struct vconn_class *class,
     ovs_assert(vconn->state != VCS_CONNECTING || class->connect);
 }
 
-void
-vconn_set_remote_ip(struct vconn *vconn, ovs_be32 ip)
-{
-    vconn->remote_ip = ip;
-}
-
-void
-vconn_set_remote_port(struct vconn *vconn, ovs_be16 port)
-{
-    vconn->remote_port = port;
-}
-
-void
-vconn_set_local_ip(struct vconn *vconn, ovs_be32 ip)
-{
-    vconn->local_ip = ip;
-}
-
-void
-vconn_set_local_port(struct vconn *vconn, ovs_be16 port)
-{
-    vconn->local_port = port;
-}
-
 void
 pvconn_init(struct pvconn *pvconn, const struct pvconn_class *class,
             const char *name, uint32_t allowed_versions)
index b15388c..8678581 100644 (file)
@@ -45,11 +45,6 @@ void vconn_set_allowed_versions(struct vconn *vconn,
 int vconn_get_version(const struct vconn *);
 void vconn_set_recv_any_version(struct vconn *);
 
-ovs_be32 vconn_get_remote_ip(const struct vconn *);
-ovs_be16 vconn_get_remote_port(const struct vconn *);
-ovs_be32 vconn_get_local_ip(const struct vconn *);
-ovs_be16 vconn_get_local_port(const struct vconn *);
-
 int vconn_connect(struct vconn *);
 int vconn_recv(struct vconn *, struct ofpbuf **);
 int vconn_send(struct vconn *, struct ofpbuf *);
index 0fa1ab2..540e72a 100644 (file)
@@ -75,24 +75,7 @@ VLOG_LEVELS
 BUILD_ASSERT_DECL(LOG_LOCAL0 == (16 << 3));
 
 /* The log modules. */
-#if USE_LINKER_SECTIONS
-extern struct vlog_module *__start_vlog_modules[];
-extern struct vlog_module *__stop_vlog_modules[];
-#define vlog_modules __start_vlog_modules
-#define n_vlog_modules (__stop_vlog_modules - __start_vlog_modules)
-#else
-#define VLOG_MODULE VLOG_DEFINE_MODULE__
-#include "vlog-modules.def"
-#undef VLOG_MODULE
-
-extern struct vlog_module *vlog_modules[];
-struct vlog_module *vlog_modules[] = {
-#define VLOG_MODULE(NAME) &VLM_##NAME,
-#include "vlog-modules.def"
-#undef VLOG_MODULE
-};
-#define n_vlog_modules ARRAY_SIZE(vlog_modules)
-#endif
+struct list vlog_modules = LIST_INITIALIZER(&vlog_modules);
 
 /* 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
@@ -199,13 +182,14 @@ vlog_get_module_name(const struct vlog_module *module)
 struct vlog_module *
 vlog_module_from_name(const char *name)
 {
-    struct vlog_module **mp;
+    struct vlog_module *mp;
 
-    for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) {
-        if (!strcasecmp(name, (*mp)->name)) {
-            return *mp;
+    LIST_FOR_EACH (mp, list, &vlog_modules) {
+        if (!strcasecmp(name, mp->name)) {
+            return mp;
         }
     }
+
     return NULL;
 }
 
@@ -242,11 +226,10 @@ set_facility_level(enum vlog_facility facility, struct vlog_module *module,
 
     ovs_mutex_lock(&log_file_mutex);
     if (!module) {
-        struct vlog_module **mp;
-
-        for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) {
-            (*mp)->levels[facility] = level;
-            update_min_level(*mp);
+        struct vlog_module *mp;
+        LIST_FOR_EACH (mp, list, &vlog_modules) {
+            mp->levels[facility] = level;
+            update_min_level(mp);
         }
     } else {
         module->levels[facility] = level;
@@ -308,7 +291,7 @@ int
 vlog_set_log_file(const char *file_name)
 {
     char *new_log_file_name;
-    struct vlog_module **mp;
+    struct vlog_module *mp;
     struct stat old_stat;
     struct stat new_stat;
     int new_log_fd;
@@ -364,8 +347,8 @@ vlog_set_log_file(const char *file_name)
         log_writer = async_append_create(new_log_fd);
     }
 
-    for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) {
-        update_min_level(*mp);
+    LIST_FOR_EACH (mp, list, &vlog_modules) {
+        update_min_level(mp);
     }
     ovs_mutex_unlock(&log_file_mutex);
 
@@ -563,10 +546,10 @@ vlog_unixctl_reopen(struct unixctl_conn *conn, int argc OVS_UNUSED,
 static void
 set_all_rate_limits(bool enable)
 {
-    struct vlog_module **mp;
+    struct vlog_module *mp;
 
-    for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) {
-        (*mp)->honor_rate_limits = enable;
+    LIST_FOR_EACH (mp, list, &vlog_modules) {
+        mp->honor_rate_limits = enable;
     }
 }
 
@@ -672,7 +655,7 @@ char *
 vlog_get_levels(void)
 {
     struct ds s = DS_EMPTY_INITIALIZER;
-    struct vlog_module **mp;
+    struct vlog_module *mp;
     struct svec lines = SVEC_EMPTY_INITIALIZER;
     char *line;
     size_t i;
@@ -680,16 +663,16 @@ vlog_get_levels(void)
     ds_put_format(&s, "                 console    syslog    file\n");
     ds_put_format(&s, "                 -------    ------    ------\n");
 
-    for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) {
+    LIST_FOR_EACH (mp, list, &vlog_modules) {
         struct ds line;
 
         ds_init(&line);
         ds_put_format(&line, "%-16s  %4s       %4s       %4s",
-                      vlog_get_module_name(*mp),
-                      vlog_get_level_name(vlog_get_level(*mp, VLF_CONSOLE)),
-                      vlog_get_level_name(vlog_get_level(*mp, VLF_SYSLOG)),
-                      vlog_get_level_name(vlog_get_level(*mp, VLF_FILE)));
-        if (!(*mp)->honor_rate_limits) {
+                      vlog_get_module_name(mp),
+                      vlog_get_level_name(vlog_get_level(mp, VLF_CONSOLE)),
+                      vlog_get_level_name(vlog_get_level(mp, VLF_SYSLOG)),
+                      vlog_get_level_name(vlog_get_level(mp, VLF_FILE)));
+        if (!mp->honor_rate_limits) {
             ds_put_cstr(&line, "    (rate limiting disabled)");
         }
         ds_put_char(&line, '\n');
index da55405..226b7f8 100644 (file)
@@ -35,6 +35,7 @@
 #include "sat-math.h"
 #include "token-bucket.h"
 #include "util.h"
+#include "list.h"
 
 #ifdef  __cplusplus
 extern "C" {
@@ -78,22 +79,22 @@ enum vlog_facility vlog_get_facility_val(const char *name);
 
 /* A log module. */
 struct vlog_module {
+    struct list list;
     const char *name;             /* User-visible name. */
     int levels[VLF_N_FACILITIES]; /* Minimum log level for each facility. */
     int min_level;                /* Minimum log level for any facility. */
     bool honor_rate_limits;       /* Set false to ignore rate limits. */
 };
 
+/* Global list of all logging modules */
+extern struct list vlog_modules;
+
 /* Creates and initializes a global instance of a module named MODULE. */
-#if USE_LINKER_SECTIONS
 #define VLOG_DEFINE_MODULE(MODULE)                                      \
         VLOG_DEFINE_MODULE__(MODULE)                                    \
-        extern struct vlog_module *const vlog_module_ptr_##MODULE;      \
-        struct vlog_module *const vlog_module_ptr_##MODULE              \
-            __attribute__((section("vlog_modules"))) = &VLM_##MODULE
-#else
-#define VLOG_DEFINE_MODULE(MODULE) extern struct vlog_module VLM_##MODULE
-#endif
+        OVS_CONSTRUCTOR(init_##MODULE) {                                \
+                list_insert(&vlog_modules, &VLM_##MODULE.list);         \
+        }                                                               \
 
 const char *vlog_get_module_name(const struct vlog_module *);
 struct vlog_module *vlog_module_from_name(const char *name);
@@ -266,6 +267,7 @@ void vlog_usage(void);
         extern struct vlog_module VLM_##MODULE;                         \
         struct vlog_module VLM_##MODULE =                               \
         {                                                               \
+            LIST_INITIALIZER(&VLM_##MODULE.list),                       \
             #MODULE,                                        /* name */  \
             { [ 0 ... VLF_N_FACILITIES - 1] = VLL_INFO }, /* levels */  \
             VLL_INFO,                                  /* min_level */  \
diff --git a/m4/.gitignore b/m4/.gitignore
new file mode 100644 (file)
index 0000000..94e6f26
--- /dev/null
@@ -0,0 +1,5 @@
+/libtool.m4
+/ltoptions.m4
+/ltsugar.m4
+/ltversion.m4
+/lt~obsolete.m4
diff --git a/m4/absolute-header.m4 b/m4/absolute-header.m4
new file mode 100644 (file)
index 0000000..89ff5be
--- /dev/null
@@ -0,0 +1,102 @@
+# absolute-header.m4 serial 16
+dnl Copyright (C) 2006-2013 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Derek Price.
+
+# gl_ABSOLUTE_HEADER(HEADER1 HEADER2 ...)
+# ---------------------------------------
+# Find the absolute name of a header file, testing first if the header exists.
+# If the header were sys/inttypes.h, this macro would define
+# ABSOLUTE_SYS_INTTYPES_H to the '""' quoted absolute name of sys/inttypes.h
+# in config.h
+# (e.g. '#define ABSOLUTE_SYS_INTTYPES_H "///usr/include/sys/inttypes.h"').
+# The three "///" are to pacify Sun C 5.8, which otherwise would say
+# "warning: #include of /usr/include/... may be non-portable".
+# Use '""', not '<>', so that the /// cannot be confused with a C99 comment.
+# Note: This macro assumes that the header file is not empty after
+# preprocessing, i.e. it does not only define preprocessor macros but also
+# provides some type/enum definitions or function/variable declarations.
+AC_DEFUN([gl_ABSOLUTE_HEADER],
+[AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_PREPROC_REQUIRE()dnl
+dnl FIXME: gl_absolute_header and ac_header_exists must be used unquoted
+dnl until we can assume autoconf 2.64 or newer.
+m4_foreach_w([gl_HEADER_NAME], [$1],
+  [AS_VAR_PUSHDEF([gl_absolute_header],
+                  [gl_cv_absolute_]m4_defn([gl_HEADER_NAME]))dnl
+  AC_CACHE_CHECK([absolute name of <]m4_defn([gl_HEADER_NAME])[>],
+    m4_defn([gl_absolute_header]),
+    [AS_VAR_PUSHDEF([ac_header_exists],
+                    [ac_cv_header_]m4_defn([gl_HEADER_NAME]))dnl
+    AC_CHECK_HEADERS_ONCE(m4_defn([gl_HEADER_NAME]))dnl
+    if test AS_VAR_GET(ac_header_exists) = yes; then
+      gl_ABSOLUTE_HEADER_ONE(m4_defn([gl_HEADER_NAME]))
+    fi
+    AS_VAR_POPDEF([ac_header_exists])dnl
+    ])dnl
+  AC_DEFINE_UNQUOTED(AS_TR_CPP([ABSOLUTE_]m4_defn([gl_HEADER_NAME])),
+                     ["AS_VAR_GET(gl_absolute_header)"],
+                     [Define this to an absolute name of <]m4_defn([gl_HEADER_NAME])[>.])
+  AS_VAR_POPDEF([gl_absolute_header])dnl
+])dnl
+])# gl_ABSOLUTE_HEADER
+
+# gl_ABSOLUTE_HEADER_ONE(HEADER)
+# ------------------------------
+# Like gl_ABSOLUTE_HEADER, except that:
+#   - it assumes that the header exists,
+#   - it uses the current CPPFLAGS,
+#   - it does not cache the result,
+#   - it is silent.
+AC_DEFUN([gl_ABSOLUTE_HEADER_ONE],
+[
+  AC_REQUIRE([AC_CANONICAL_HOST])
+  AC_LANG_CONFTEST([AC_LANG_SOURCE([[#include <]]m4_dquote([$1])[[>]])])
+  dnl AIX "xlc -E" and "cc -E" omit #line directives for header files
+  dnl that contain only a #include of other header files and no
+  dnl non-comment tokens of their own. This leads to a failure to
+  dnl detect the absolute name of <dirent.h>, <signal.h>, <poll.h>
+  dnl and others. The workaround is to force preservation of comments
+  dnl through option -C. This ensures all necessary #line directives
+  dnl are present. GCC supports option -C as well.
+  case "$host_os" in
+    aix*) gl_absname_cpp="$ac_cpp -C" ;;
+    *)    gl_absname_cpp="$ac_cpp" ;;
+  esac
+changequote(,)
+  case "$host_os" in
+    mingw*)
+      dnl For the sake of native Windows compilers (excluding gcc),
+      dnl treat backslash as a directory separator, like /.
+      dnl Actually, these compilers use a double-backslash as
+      dnl directory separator, inside the
+      dnl   # line "filename"
+      dnl directives.
+      gl_dirsep_regex='[/\\]'
+      ;;
+    *)
+      gl_dirsep_regex='\/'
+      ;;
+  esac
+  dnl A sed expression that turns a string into a basic regular
+  dnl expression, for use within "/.../".
+  gl_make_literal_regex_sed='s,[]$^\\.*/[],\\&,g'
+  gl_header_literal_regex=`echo '$1' \
+                           | sed -e "$gl_make_literal_regex_sed"`
+  gl_absolute_header_sed="/${gl_dirsep_regex}${gl_header_literal_regex}/"'{
+      s/.*"\(.*'"${gl_dirsep_regex}${gl_header_literal_regex}"'\)".*/\1/
+      s|^/[^/]|//&|
+      p
+      q
+    }'
+changequote([,])
+  dnl eval is necessary to expand gl_absname_cpp.
+  dnl Ultrix and Pyramid sh refuse to redirect output of eval,
+  dnl so use subshell.
+  AS_VAR_SET([gl_cv_absolute_]AS_TR_SH([[$1]]),
+[`(eval "$gl_absname_cpp conftest.$ac_ext") 2>&AS_MESSAGE_LOG_FD |
+  sed -n "$gl_absolute_header_sed"`])
+])
diff --git a/m4/automake.mk b/m4/automake.mk
new file mode 100644 (file)
index 0000000..672a221
--- /dev/null
@@ -0,0 +1,10 @@
+# Copyright (C) 2013 Nicira, Inc.
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.  This file is offered as-is,
+# without warranty of any kind.
+
+EXTRA_DIST += \
+       m4/absolute-header.m4 \
+       m4/include_next.m4
diff --git a/m4/include_next.m4 b/m4/include_next.m4
new file mode 100644 (file)
index 0000000..f09dbe6
--- /dev/null
@@ -0,0 +1,223 @@
+# include_next.m4 serial 23
+dnl Copyright (C) 2006-2013 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Paul Eggert and Derek Price.
+
+dnl Sets INCLUDE_NEXT and PRAGMA_SYSTEM_HEADER.
+dnl
+dnl INCLUDE_NEXT expands to 'include_next' if the compiler supports it, or to
+dnl 'include' otherwise.
+dnl
+dnl INCLUDE_NEXT_AS_FIRST_DIRECTIVE expands to 'include_next' if the compiler
+dnl supports it in the special case that it is the first include directive in
+dnl the given file, or to 'include' otherwise.
+dnl
+dnl PRAGMA_SYSTEM_HEADER can be used in files that contain #include_next,
+dnl so as to avoid GCC warnings when the gcc option -pedantic is used.
+dnl '#pragma GCC system_header' has the same effect as if the file was found
+dnl through the include search path specified with '-isystem' options (as
+dnl opposed to the search path specified with '-I' options). Namely, gcc
+dnl does not warn about some things, and on some systems (Solaris and Interix)
+dnl __STDC__ evaluates to 0 instead of to 1. The latter is an undesired side
+dnl effect; we are therefore careful to use 'defined __STDC__' or '1' instead
+dnl of plain '__STDC__'.
+dnl
+dnl PRAGMA_COLUMNS can be used in files that override system header files, so
+dnl as to avoid compilation errors on HP NonStop systems when the gnulib file
+dnl is included by a system header file that does a "#pragma COLUMNS 80" (which
+dnl has the effect of truncating the lines of that file and all files that it
+dnl includes to 80 columns) and the gnulib file has lines longer than 80
+dnl columns.
+
+AC_DEFUN([gl_INCLUDE_NEXT],
+[
+  AC_LANG_PREPROC_REQUIRE()
+  AC_CACHE_CHECK([whether the preprocessor supports include_next],
+    [gl_cv_have_include_next],
+    [rm -rf conftestd1a conftestd1b conftestd2
+     mkdir conftestd1a conftestd1b conftestd2
+     dnl IBM C 9.0, 10.1 (original versions, prior to the 2009-01 updates) on
+     dnl AIX 6.1 support include_next when used as first preprocessor directive
+     dnl in a file, but not when preceded by another include directive. Check
+     dnl for this bug by including <stdio.h>.
+     dnl Additionally, with this same compiler, include_next is a no-op when
+     dnl used in a header file that was included by specifying its absolute
+     dnl file name. Despite these two bugs, include_next is used in the
+     dnl compiler's <math.h>. By virtue of the second bug, we need to use
+     dnl include_next as well in this case.
+     cat <<EOF > conftestd1a/conftest.h
+#define DEFINED_IN_CONFTESTD1
+#include_next <conftest.h>
+#ifdef DEFINED_IN_CONFTESTD2
+int foo;
+#else
+#error "include_next doesn't work"
+#endif
+EOF
+     cat <<EOF > conftestd1b/conftest.h
+#define DEFINED_IN_CONFTESTD1
+#include <stdio.h>
+#include_next <conftest.h>
+#ifdef DEFINED_IN_CONFTESTD2
+int foo;
+#else
+#error "include_next doesn't work"
+#endif
+EOF
+     cat <<EOF > conftestd2/conftest.h
+#ifndef DEFINED_IN_CONFTESTD1
+#error "include_next test doesn't work"
+#endif
+#define DEFINED_IN_CONFTESTD2
+EOF
+     gl_save_CPPFLAGS="$CPPFLAGS"
+     CPPFLAGS="$gl_save_CPPFLAGS -Iconftestd1b -Iconftestd2"
+dnl We intentionally avoid using AC_LANG_SOURCE here.
+     AC_COMPILE_IFELSE([AC_LANG_DEFINES_PROVIDED[#include <conftest.h>]],
+       [gl_cv_have_include_next=yes],
+       [CPPFLAGS="$gl_save_CPPFLAGS -Iconftestd1a -Iconftestd2"
+        AC_COMPILE_IFELSE([AC_LANG_DEFINES_PROVIDED[#include <conftest.h>]],
+          [gl_cv_have_include_next=buggy],
+          [gl_cv_have_include_next=no])
+       ])
+     CPPFLAGS="$gl_save_CPPFLAGS"
+     rm -rf conftestd1a conftestd1b conftestd2
+    ])
+  PRAGMA_SYSTEM_HEADER=
+  if test $gl_cv_have_include_next = yes; then
+    INCLUDE_NEXT=include_next
+    INCLUDE_NEXT_AS_FIRST_DIRECTIVE=include_next
+    if test -n "$GCC"; then
+      PRAGMA_SYSTEM_HEADER='#pragma GCC system_header'
+    fi
+  else
+    if test $gl_cv_have_include_next = buggy; then
+      INCLUDE_NEXT=include
+      INCLUDE_NEXT_AS_FIRST_DIRECTIVE=include_next
+    else
+      INCLUDE_NEXT=include
+      INCLUDE_NEXT_AS_FIRST_DIRECTIVE=include
+    fi
+  fi
+  AC_SUBST([INCLUDE_NEXT])
+  AC_SUBST([INCLUDE_NEXT_AS_FIRST_DIRECTIVE])
+  AC_SUBST([PRAGMA_SYSTEM_HEADER])
+  AC_CACHE_CHECK([whether system header files limit the line length],
+    [gl_cv_pragma_columns],
+    [dnl HP NonStop systems, which define __TANDEM, have this misfeature.
+     AC_EGREP_CPP([choke me],
+       [
+#ifdef __TANDEM
+choke me
+#endif
+       ],
+       [gl_cv_pragma_columns=yes],
+       [gl_cv_pragma_columns=no])
+    ])
+  if test $gl_cv_pragma_columns = yes; then
+    PRAGMA_COLUMNS="#pragma COLUMNS 10000"
+  else
+    PRAGMA_COLUMNS=
+  fi
+  AC_SUBST([PRAGMA_COLUMNS])
+])
+
+# gl_CHECK_NEXT_HEADERS(HEADER1 HEADER2 ...)
+# ------------------------------------------
+# For each arg foo.h, if #include_next works, define NEXT_FOO_H to be
+# '<foo.h>'; otherwise define it to be
+# '"///usr/include/foo.h"', or whatever other absolute file name is suitable.
+# Also, if #include_next works as first preprocessing directive in a file,
+# define NEXT_AS_FIRST_DIRECTIVE_FOO_H to be '<foo.h>'; otherwise define it to
+# be
+# '"///usr/include/foo.h"', or whatever other absolute file name is suitable.
+# That way, a header file with the following line:
+#       #@INCLUDE_NEXT@ @NEXT_FOO_H@
+# or
+#       #@INCLUDE_NEXT_AS_FIRST_DIRECTIVE@ @NEXT_AS_FIRST_DIRECTIVE_FOO_H@
+# behaves (after sed substitution) as if it contained
+#       #include_next <foo.h>
+# even if the compiler does not support include_next.
+# The three "///" are to pacify Sun C 5.8, which otherwise would say
+# "warning: #include of /usr/include/... may be non-portable".
+# Use '""', not '<>', so that the /// cannot be confused with a C99 comment.
+# Note: This macro assumes that the header file is not empty after
+# preprocessing, i.e. it does not only define preprocessor macros but also
+# provides some type/enum definitions or function/variable declarations.
+#
+# This macro also checks whether each header exists, by invoking
+# AC_CHECK_HEADERS_ONCE or AC_CHECK_HEADERS on each argument.
+AC_DEFUN([gl_CHECK_NEXT_HEADERS],
+[
+  gl_NEXT_HEADERS_INTERNAL([$1], [check])
+])
+
+# gl_NEXT_HEADERS(HEADER1 HEADER2 ...)
+# ------------------------------------
+# Like gl_CHECK_NEXT_HEADERS, except do not check whether the headers exist.
+# This is suitable for headers like <stddef.h> that are standardized by C89
+# and therefore can be assumed to exist.
+AC_DEFUN([gl_NEXT_HEADERS],
+[
+  gl_NEXT_HEADERS_INTERNAL([$1], [assume])
+])
+
+# The guts of gl_CHECK_NEXT_HEADERS and gl_NEXT_HEADERS.
+AC_DEFUN([gl_NEXT_HEADERS_INTERNAL],
+[
+  AC_REQUIRE([gl_INCLUDE_NEXT])
+  AC_REQUIRE([AC_CANONICAL_HOST])
+
+  m4_if([$2], [check],
+    [AC_CHECK_HEADERS_ONCE([$1])
+    ])
+
+dnl FIXME: gl_next_header and gl_header_exists must be used unquoted
+dnl until we can assume autoconf 2.64 or newer.
+  m4_foreach_w([gl_HEADER_NAME], [$1],
+    [AS_VAR_PUSHDEF([gl_next_header],
+                    [gl_cv_next_]m4_defn([gl_HEADER_NAME]))
+     if test $gl_cv_have_include_next = yes; then
+       AS_VAR_SET(gl_next_header, ['<'gl_HEADER_NAME'>'])
+     else
+       AC_CACHE_CHECK(
+         [absolute name of <]m4_defn([gl_HEADER_NAME])[>],
+         m4_defn([gl_next_header]),
+         [m4_if([$2], [check],
+            [AS_VAR_PUSHDEF([gl_header_exists],
+                            [ac_cv_header_]m4_defn([gl_HEADER_NAME]))
+             if test AS_VAR_GET(gl_header_exists) = yes; then
+             AS_VAR_POPDEF([gl_header_exists])
+            ])
+           gl_ABSOLUTE_HEADER_ONE(gl_HEADER_NAME)
+           AS_VAR_COPY([gl_header], [gl_cv_absolute_]AS_TR_SH(gl_HEADER_NAME))
+           AS_VAR_SET(gl_next_header, ['"'$gl_header'"'])
+          m4_if([$2], [check],
+            [else
+               AS_VAR_SET(gl_next_header, ['<'gl_HEADER_NAME'>'])
+             fi
+            ])
+         ])
+     fi
+     AC_SUBST(
+       AS_TR_CPP([NEXT_]m4_defn([gl_HEADER_NAME])),
+       [AS_VAR_GET(gl_next_header)])
+     if test $gl_cv_have_include_next = yes || test $gl_cv_have_include_next = buggy; then
+       # INCLUDE_NEXT_AS_FIRST_DIRECTIVE='include_next'
+       gl_next_as_first_directive='<'gl_HEADER_NAME'>'
+     else
+       # INCLUDE_NEXT_AS_FIRST_DIRECTIVE='include'
+       gl_next_as_first_directive=AS_VAR_GET(gl_next_header)
+     fi
+     AC_SUBST(
+       AS_TR_CPP([NEXT_AS_FIRST_DIRECTIVE_]m4_defn([gl_HEADER_NAME])),
+       [$gl_next_as_first_directive])
+     AS_VAR_POPDEF([gl_next_header])])
+])
+
+# Autoconf 2.68 added warnings for our use of AC_COMPILE_IFELSE;
+# this fallback is safe for all earlier autoconf versions.
+m4_define_default([AC_LANG_DEFINES_PROVIDED])
index e7281ad..ee7e96a 100644 (file)
@@ -64,6 +64,9 @@ AC_DEFUN([OVS_CHECK_WIN32],
    AM_CONDITIONAL([WIN32], [test "$WIN32" = yes])
    if test "$WIN32" = yes; then
       AC_DEFINE([WIN32], [1], [Define to 1 if building on WIN32.])
+      AH_BOTTOM([#ifdef WIN32
+#include "include/windows/windefs.h"
+#endif])
    fi])
 
 dnl Checks for Netlink support.
@@ -308,83 +311,6 @@ AC_DEFUN([OVS_CHECK_PYTHON_COMPAT],
    AC_MSG_RESULT([$INCLUDE_PYTHON_COMPAT])
    AM_CONDITIONAL([INCLUDE_PYTHON_COMPAT], [test $INCLUDE_PYTHON_COMPAT = yes])])
 
-# OVS_LINK2_IFELSE(SOURCE1, SOURCE2, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
-# -------------------------------------------------------------
-# Based on AC_LINK_IFELSE, but tries to link both SOURCE1 and SOURCE2
-# into a program.
-#
-# This macro is borrowed from acinclude.m4 in GNU PSPP, which has the
-# following license:
-#
-#     Copyright (C) 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
-#     This file is free software; the Free Software Foundation
-#     gives unlimited permission to copy and/or distribute it,
-#     with or without modifications, as long as this notice is preserved.
-#
-m4_define([OVS_LINK2_IFELSE],
-[m4_ifvaln([$1], [AC_LANG_CONFTEST([$1])])dnl
-mv conftest.$ac_ext conftest1.$ac_ext
-m4_ifvaln([$2], [AC_LANG_CONFTEST([$2])])dnl
-mv conftest.$ac_ext conftest2.$ac_ext
-rm -f conftest1.$ac_objext conftest2.$ac_objext conftest$ac_exeext
-ovs_link2='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest1.$ac_ext conftest2.$ac_ext $LIBS >&5'
-AS_IF([_AC_DO_STDERR($ovs_link2) && {
-        test -z "$ac_[]_AC_LANG_ABBREV[]_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest$ac_exeext && {
-        test "$cross_compiling" = yes ||
-        AS_TEST_X([conftest$ac_exeext])
-       }],
-      [$3],
-      [echo "$as_me: failed source file 1 of 2 was:" >&5
-sed 's/^/| /' conftest1.$ac_ext >&5
-echo "$as_me: failed source file 2 of 2 was:" >&5
-sed 's/^/| /' conftest2.$ac_ext >&5
-       $4])
-dnl Delete also the IPA/IPO (Inter Procedural Analysis/Optimization)
-dnl information created by the PGI compiler (conftest_ipa8_conftest.oo),
-dnl as it would interfere with the next link command.
-rm -rf conftest.dSYM conftest1.dSYM conftest2.dSYM
-rm -f core conftest.err conftest1.err conftest2.err
-rm -f conftest1.$ac_objext conftest2.$ac_objext conftest*_ipa8_conftest*.oo
-rm -f conftest$ac_exeext
-rm -f m4_ifval([$1], [conftest1.$ac_ext]) m4_ifval([$2], [conftest1.$ac_ext])[]dnl
-])# OVS_LINK2_IFELSE
-
-dnl Defines USE_LINKER_SECTIONS to 1 if the compiler supports putting
-dnl variables in sections with user-defined names and the linker
-dnl automatically defines __start_SECNAME and __stop_SECNAME symbols
-dnl that designate the start and end of the sections.
-AC_DEFUN([OVS_CHECK_LINKER_SECTIONS],
-  [AC_CACHE_CHECK(
-    [for user-defined linker section support],
-    [ovs_cv_use_linker_sections],
-    [OVS_LINK2_IFELSE(
-      [AC_LANG_SOURCE(
-        [int a __attribute__((__section__("mysection"))) = 1;
-         int b __attribute__((__section__("mysection"))) = 2;
-         int c __attribute__((__section__("mysection"))) = 3;])],
-      [AC_LANG_PROGRAM(
-        [#include <stdio.h>
-         extern int __start_mysection;
-         extern int __stop_mysection;],
-        [int n_ints = &__stop_mysection - &__start_mysection;
-         int *i;
-         for (i = &__start_mysection; i < &__start_mysection + n_ints; i++) {
-             printf("%d\n", *i);
-         }])],
-      [ovs_cv_use_linker_sections=yes],
-      [ovs_cv_use_linker_sections=no])])
-   if test $ovs_cv_use_linker_sections = yes; then
-     AC_DEFINE([USE_LINKER_SECTIONS], [1],
-               [Define to 1 if the compiler support putting variables
-                into sections with user-defined names and the linker
-                automatically defines __start_SECNAME and __stop_SECNAME
-                symbols that designate the start and end of the section.])
-   fi
-   AM_CONDITIONAL(
-     [USE_LINKER_SECTIONS], [test $ovs_cv_use_linker_sections = yes])])
-
 dnl Checks for groff.
 AC_DEFUN([OVS_CHECK_GROFF],
   [AC_CACHE_CHECK(
@@ -535,3 +461,8 @@ 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])])
+
+dnl OVS_CHECK_INCLUDE_NEXT
+AC_DEFUN([OVS_CHECK_INCLUDE_NEXT],
+  [AC_REQUIRE([gl_CHECK_NEXT_HEADERS])
+   gl_CHECK_NEXT_HEADERS([$1])])
index 218a5b1..1308820 100644 (file)
@@ -5,8 +5,9 @@
 # notice and this notice are preserved.  This file is offered as-is,
 # without warranty of any kind.
 
-noinst_LIBRARIES += ofproto/libofproto.a
-ofproto_libofproto_a_SOURCES = \
+lib_LTLIBRARIES += ofproto/libofproto.la
+ofproto_libofproto_la_LDFLAGS = -release $(VERSION)
+ofproto_libofproto_la_SOURCES = \
        ofproto/bond.c \
        ofproto/bond.h \
        ofproto/collectors.c \
@@ -24,8 +25,6 @@ ofproto_libofproto_a_SOURCES = \
        ofproto/ofproto.h \
        ofproto/ofproto-dpif.c \
        ofproto/ofproto-dpif.h \
-       ofproto/ofproto-dpif-governor.c \
-       ofproto/ofproto-dpif-governor.h \
        ofproto/ofproto-dpif-ipfix.c \
        ofproto/ofproto-dpif-ipfix.h \
        ofproto/ofproto-dpif-mirror.c \
@@ -45,10 +44,13 @@ ofproto_libofproto_a_SOURCES = \
        ofproto/pinsched.h \
        ofproto/tunnel.c \
        ofproto/tunnel.h
+ofproto_libofproto_la_CPPFLAGS = $(AM_CPPFLAGS)
+ofproto_libofproto_la_CFLAGS = $(AM_CFLAGS)
+ofproto_libofproto_la_LIBADD = lib/libsflow.la
 
 # Distribute this generated file in order not to require Python at
 # build time if ofproto/ipfix.xml is not modified.
-ofproto_libofproto_a_SOURCES += ofproto/ipfix-entities.def
+ofproto_libofproto_la_SOURCES += ofproto/ipfix-entities.def
 
 BUILT_SOURCES += ofproto/ipfix-entities.def
 
@@ -60,4 +62,5 @@ MAN_FRAGMENTS += ofproto/ofproto-unixctl.man ofproto/ofproto-dpif-unixctl.man
 EXTRA_DIST += ofproto/ipfix.xml
 dist_noinst_SCRIPTS = ofproto/ipfix-gen-entities
 ofproto/ipfix-entities.def: ofproto/ipfix.xml ofproto/ipfix-gen-entities
-       $(run_python) $(srcdir)/ofproto/ipfix-gen-entities $< > $@
+       $(run_python) $(srcdir)/ofproto/ipfix-gen-entities $< > $@.tmp
+       mv $@.tmp $@
index dc0d76b..ff050f1 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <math.h>
 
+#include "connectivity.h"
 #include "coverage.h"
 #include "dynamic-string.h"
 #include "flow.h"
@@ -34,6 +35,7 @@
 #include "ofpbuf.h"
 #include "packets.h"
 #include "poll-loop.h"
+#include "seq.h"
 #include "shash.h"
 #include "timeval.h"
 #include "unixctl.h"
@@ -162,7 +164,7 @@ bond_mode_to_string(enum bond_mode balance) {
     case BM_AB:
         return "active-backup";
     }
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 \f
@@ -441,7 +443,7 @@ bond_run(struct bond *bond, enum lacp_status lacp_status)
     /* Enable slaves based on link status and LACP feedback. */
     HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
         bond_link_status_update(slave);
-        slave->change_seq = netdev_change_seq(slave->netdev);
+        slave->change_seq = seq_read(connectivity_seq_get());
     }
     if (!bond->active_slave || !bond->active_slave->enabled) {
         bond_choose_active_slave(bond);
@@ -472,9 +474,7 @@ bond_wait(struct bond *bond)
             poll_timer_wait_until(slave->delay_expires);
         }
 
-        if (slave->change_seq != netdev_change_seq(slave->netdev)) {
-            poll_immediate_wake();
-        }
+        seq_wait(connectivity_seq_get(), slave->change_seq);
     }
 
     if (bond->next_fake_iface_update != LLONG_MAX) {
@@ -647,7 +647,7 @@ bond_check_admissibility(struct bond *bond, const void *slave_,
         goto out;
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 out:
     ovs_rwlock_unlock(&rwlock);
     return verdict;
@@ -1468,7 +1468,7 @@ choose_output_slave(const struct bond *bond, const struct flow *flow,
         return e->slave;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
index da25930..ea79795 100644 (file)
@@ -637,12 +637,13 @@ connmgr_set_controllers(struct connmgr *mgr,
 
     shash_destroy(&new_controllers);
 
+    ovs_mutex_unlock(&ofproto_mutex);
+
     update_in_band_remotes(mgr);
     update_fail_open(mgr);
     if (had_controllers != connmgr_has_controllers(mgr)) {
         ofproto_flush_flows(mgr->ofproto);
     }
-    ovs_mutex_unlock(&ofproto_mutex);
 }
 
 /* Drops the connections between 'mgr' and all of its primary and secondary
@@ -1549,8 +1550,13 @@ do_send_packet_ins(struct ofconn *ofconn, struct list *txq)
     LIST_FOR_EACH_SAFE (pin, next_pin, list_node, txq) {
         list_remove(&pin->list_node);
 
-        rconn_send_with_limit(ofconn->rconn, pin,
-                              ofconn->packet_in_counter, 100);
+        if (rconn_send_with_limit(ofconn->rconn, pin,
+                                  ofconn->packet_in_counter, 100) == EAGAIN) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
+
+            VLOG_INFO_RL(&rl, "%s: dropping packet-in due to queue overflow",
+                         rconn_get_name(ofconn->rconn));
+        }
     }
 }
 
@@ -1965,7 +1971,7 @@ ofmonitor_report(struct connmgr *mgr, struct rule *rule,
 
     default:
     case NXFME_ABBREV:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
index a094bac..f79ad6a 100644 (file)
@@ -22,6 +22,7 @@
 #include <unistd.h>
 #include "byte-order.h"
 #include "collectors.h"
+#include "dpif.h"
 #include "flow.h"
 #include "lib/netflow.h"
 #include "ofpbuf.h"
@@ -49,8 +50,45 @@ struct netflow {
     long long int active_timeout; /* Timeout for flows that are still active. */
     long long int next_timeout;   /* Next scheduled active timeout. */
     long long int reconfig_time;  /* When we reconfigured the timeouts. */
+
+    struct hmap flows;            /* Contains 'netflow_flows'. */
+
+    atomic_int ref_cnt;
+};
+
+struct netflow_flow {
+    struct hmap_node hmap_node;
+
+    long long int last_expired;   /* Time this flow last timed out. */
+    long long int created;        /* Time flow was created since time out. */
+
+    ofp_port_t output_iface;      /* Output interface index. */
+    uint16_t tcp_flags;           /* Bitwise-OR of all TCP flags seen. */
+
+    ofp_port_t in_port;           /* Input port. */
+    ovs_be32 nw_src;              /* IPv4 source address. */
+    ovs_be32 nw_dst;              /* IPv4 destination address. */
+    uint8_t nw_tos;               /* IP ToS (including DSCP and ECN). */
+    uint8_t nw_proto;             /* IP protocol. */
+    ovs_be16 tp_src;              /* TCP/UDP/SCTP source port. */
+    ovs_be16 tp_dst;              /* TCP/UDP/SCTP destination port. */
+
+    uint64_t packet_count;        /* Packets from subrules. */
+    uint64_t byte_count;          /* Bytes from subrules. */
+    long long int used;           /* Last-used time (0 if never used). */
 };
 
+static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
+static atomic_uint netflow_count = ATOMIC_VAR_INIT(0);
+
+static struct netflow_flow *netflow_flow_lookup(const struct netflow *,
+                                                const struct flow *)
+    OVS_REQUIRES(mutex);
+static uint32_t netflow_flow_hash(const struct flow *);
+static void netflow_expire__(struct netflow *, struct netflow_flow *)
+    OVS_REQUIRES(mutex);
+static void netflow_run__(struct netflow *) OVS_REQUIRES(mutex);
+
 void
 netflow_mask_wc(struct flow *flow, struct flow_wildcards *wc)
 {
@@ -60,15 +98,14 @@ netflow_mask_wc(struct flow *flow, struct flow_wildcards *wc)
     memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
     memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
     memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
-    memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
-    memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
+    flow_unwildcard_tp_ports(flow, wc);
     wc->masks.nw_tos |= IP_DSCP_MASK;
 }
 
 static void
 gen_netflow_rec(struct netflow *nf, struct netflow_flow *nf_flow,
-                struct ofexpired *expired,
                 uint32_t packet_count, uint32_t byte_count)
+    OVS_REQUIRES(mutex)
 {
     struct netflow_v5_header *nf_hdr;
     struct netflow_v5_record *nf_rec;
@@ -94,75 +131,128 @@ gen_netflow_rec(struct netflow *nf, struct netflow_flow *nf_flow,
     nf_hdr->count = htons(ntohs(nf_hdr->count) + 1);
 
     nf_rec = ofpbuf_put_zeros(&nf->packet, sizeof *nf_rec);
-    nf_rec->src_addr = expired->flow.nw_src;
-    nf_rec->dst_addr = expired->flow.nw_dst;
+    nf_rec->src_addr = nf_flow->nw_src;
+    nf_rec->dst_addr = nf_flow->nw_dst;
     nf_rec->nexthop = htonl(0);
     if (nf->add_id_to_iface) {
         uint16_t iface = (nf->engine_id & 0x7f) << 9;
-        nf_rec->input = htons(iface
-            | (ofp_to_u16(expired->flow.in_port.ofp_port) & 0x1ff));
+        nf_rec->input = htons(iface | (ofp_to_u16(nf_flow->in_port) & 0x1ff));
         nf_rec->output = htons(iface
             | (ofp_to_u16(nf_flow->output_iface) & 0x1ff));
     } else {
-        nf_rec->input = htons(ofp_to_u16(expired->flow.in_port.ofp_port));
+        nf_rec->input = htons(ofp_to_u16(nf_flow->in_port));
         nf_rec->output = htons(ofp_to_u16(nf_flow->output_iface));
     }
     nf_rec->packet_count = htonl(packet_count);
     nf_rec->byte_count = htonl(byte_count);
     nf_rec->init_time = htonl(nf_flow->created - nf->boot_time);
-    nf_rec->used_time = htonl(MAX(nf_flow->created, expired->used)
+    nf_rec->used_time = htonl(MAX(nf_flow->created, nf_flow->used)
                              - nf->boot_time);
-    if (expired->flow.nw_proto == IPPROTO_ICMP) {
+    if (nf_flow->nw_proto == IPPROTO_ICMP) {
         /* In NetFlow, the ICMP type and code are concatenated and
          * placed in the 'dst_port' field. */
-        uint8_t type = ntohs(expired->flow.tp_src);
-        uint8_t code = ntohs(expired->flow.tp_dst);
+        uint8_t type = ntohs(nf_flow->tp_src);
+        uint8_t code = ntohs(nf_flow->tp_dst);
         nf_rec->src_port = htons(0);
         nf_rec->dst_port = htons((type << 8) | code);
     } else {
-        nf_rec->src_port = expired->flow.tp_src;
-        nf_rec->dst_port = expired->flow.tp_dst;
+        nf_rec->src_port = nf_flow->tp_src;
+        nf_rec->dst_port = nf_flow->tp_dst;
     }
-    nf_rec->tcp_flags = (uint8_t)nf_flow->tcp_flags;
-    nf_rec->ip_proto = expired->flow.nw_proto;
-    nf_rec->ip_tos = expired->flow.nw_tos & IP_DSCP_MASK;
+    nf_rec->tcp_flags = (uint8_t) nf_flow->tcp_flags;
+    nf_rec->ip_proto = nf_flow->nw_proto;
+    nf_rec->ip_tos = nf_flow->nw_tos & IP_DSCP_MASK;
 
     /* NetFlow messages are limited to 30 records. */
     if (ntohs(nf_hdr->count) >= 30) {
-        netflow_run(nf);
+        netflow_run__(nf);
     }
 }
 
 void
-netflow_expire(struct netflow *nf, struct netflow_flow *nf_flow,
-               struct ofexpired *expired)
+netflow_flow_update(struct netflow *nf, struct flow *flow,
+                    ofp_port_t output_iface,
+                    const struct dpif_flow_stats *stats)
+    OVS_EXCLUDED(mutex)
+{
+    struct netflow_flow *nf_flow;
+    long long int used;
+
+    /* NetFlow only reports on IP packets. */
+    if (flow->dl_type != htons(ETH_TYPE_IP)) {
+        return;
+    }
+
+    ovs_mutex_lock(&mutex);
+    nf_flow = netflow_flow_lookup(nf, flow);
+    if (!nf_flow) {
+        nf_flow = xzalloc(sizeof *nf_flow);
+        nf_flow->in_port = flow->in_port.ofp_port;
+        nf_flow->nw_src = flow->nw_src;
+        nf_flow->nw_dst = flow->nw_dst;
+        nf_flow->nw_tos = flow->nw_tos;
+        nf_flow->nw_proto = flow->nw_proto;
+        nf_flow->tp_src = flow->tp_src;
+        nf_flow->tp_dst = flow->tp_dst;
+        nf_flow->created = stats->used;
+        nf_flow->output_iface = output_iface;
+        hmap_insert(&nf->flows, &nf_flow->hmap_node, netflow_flow_hash(flow));
+    }
+
+    if (nf_flow->output_iface != output_iface) {
+        netflow_expire__(nf, nf_flow);
+        nf_flow->created = stats->used;
+        nf_flow->output_iface = output_iface;
+    }
+
+    nf_flow->packet_count += stats->n_packets;
+    nf_flow->byte_count += stats->n_bytes;
+    nf_flow->tcp_flags |= stats->tcp_flags;
+
+    used = MAX(nf_flow->used, stats->used);
+    if (nf_flow->used != used) {
+        nf_flow->used = used;
+        if (!nf->active_timeout || !nf_flow->last_expired
+            || nf->reconfig_time > nf_flow->last_expired) {
+            /* Keep the time updated to prevent a flood of expiration in
+             * the future. */
+            nf_flow->last_expired = time_msec();
+        }
+    }
+
+    ovs_mutex_unlock(&mutex);
+}
+
+static void
+netflow_expire__(struct netflow *nf, struct netflow_flow *nf_flow)
+    OVS_REQUIRES(mutex)
 {
-    uint64_t pkt_delta = expired->packet_count - nf_flow->packet_count_off;
-    uint64_t byte_delta = expired->byte_count - nf_flow->byte_count_off;
+    uint64_t pkts, bytes;
+
+    pkts = nf_flow->packet_count;
+    bytes = nf_flow->byte_count;
 
     nf_flow->last_expired += nf->active_timeout;
 
-    /* NetFlow only reports on IP packets and we should only report flows
-     * that actually have traffic. */
-    if (expired->flow.dl_type != htons(ETH_TYPE_IP) || pkt_delta == 0) {
+    if (pkts == 0) {
         return;
     }
 
-    if ((byte_delta >> 32) <= 175) {
+    if ((bytes >> 32) <= 175) {
         /* NetFlow v5 records are limited to 32-bit counters.  If we've wrapped
          * a counter, send as multiple records so we don't lose track of any
          * traffic.  We try to evenly distribute the packet and byte counters,
          * so that the bytes-per-packet lengths don't look wonky across the
          * records. */
-        while (byte_delta) {
-            int n_recs = (byte_delta + UINT32_MAX - 1) / UINT32_MAX;
-            uint32_t pkt_count = pkt_delta / n_recs;
-            uint32_t byte_count = byte_delta / n_recs;
+        while (bytes) {
+            int n_recs = (bytes + UINT32_MAX - 1) / UINT32_MAX;
+            uint32_t pkt_count = pkts / n_recs;
+            uint32_t byte_count = bytes / n_recs;
 
-            gen_netflow_rec(nf, nf_flow, expired, pkt_count, byte_count);
+            gen_netflow_rec(nf, nf_flow, pkt_count, byte_count);
 
-            pkt_delta -= pkt_count;
-            byte_delta -= byte_count;
+            pkts -= pkt_count;
+            bytes -= byte_count;
         }
     } else {
         /* In 600 seconds, a 10GbE link can theoretically transmit 75 * 10**10
@@ -176,52 +266,109 @@ netflow_expire(struct netflow *nf, struct netflow_flow *nf_flow,
          */
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
 
-        VLOG_WARN_RL(&rl, "impossible byte counter %"PRIu64, byte_delta);
+        VLOG_WARN_RL(&rl, "impossible byte counter %"PRIu64, bytes);
     }
 
     /* Update flow tracking data. */
     nf_flow->created = 0;
-    nf_flow->packet_count_off = expired->packet_count;
-    nf_flow->byte_count_off = expired->byte_count;
+    nf_flow->packet_count = 0;
+    nf_flow->byte_count = 0;
     nf_flow->tcp_flags = 0;
 }
 
+void
+netflow_expire(struct netflow *nf, struct flow *flow) OVS_EXCLUDED(mutex)
+{
+    struct netflow_flow *nf_flow;
+
+    ovs_mutex_lock(&mutex);
+    nf_flow = netflow_flow_lookup(nf, flow);
+    if (nf_flow) {
+        netflow_expire__(nf, nf_flow);
+    }
+    ovs_mutex_unlock(&mutex);
+}
+
+void
+netflow_flow_clear(struct netflow *nf, struct flow *flow) OVS_EXCLUDED(mutex)
+{
+    struct netflow_flow *nf_flow;
+
+    ovs_mutex_lock(&mutex);
+    nf_flow = netflow_flow_lookup(nf, flow);
+    if (nf_flow) {
+        ovs_assert(!nf_flow->packet_count);
+        ovs_assert(!nf_flow->byte_count);
+        hmap_remove(&nf->flows, &nf_flow->hmap_node);
+        free(nf_flow);
+    }
+    ovs_mutex_unlock(&mutex);
+}
+
 /* Returns true if it's time to send out a round of NetFlow active timeouts,
  * false otherwise. */
-bool
-netflow_run(struct netflow *nf)
+static void
+netflow_run__(struct netflow *nf) OVS_REQUIRES(mutex)
 {
+    long long int now = time_msec();
+    struct netflow_flow *nf_flow, *next;
+
     if (nf->packet.size) {
         collectors_send(nf->collectors, nf->packet.data, nf->packet.size);
         nf->packet.size = 0;
     }
 
-    if (nf->active_timeout && time_msec() >= nf->next_timeout) {
-        nf->next_timeout = time_msec() + 1000;
-        return true;
-    } else {
-        return false;
+    if (!nf->active_timeout || now < nf->next_timeout) {
+        return;
+    }
+
+    nf->next_timeout = now + 1000;
+
+    HMAP_FOR_EACH_SAFE (nf_flow, next, hmap_node, &nf->flows) {
+        if (now > nf_flow->last_expired + nf->active_timeout) {
+            bool idle = nf_flow->used < nf_flow->last_expired;
+            netflow_expire__(nf, nf_flow);
+
+            if (idle) {
+                /* If the netflow_flow hasn't been used in a while, it's
+                 * possible the upper layer lost track of it. */
+                hmap_remove(&nf->flows, &nf_flow->hmap_node);
+                free(nf_flow);
+            }
+        }
     }
 }
 
 void
-netflow_wait(struct netflow *nf)
+netflow_run(struct netflow *nf)
+{
+    ovs_mutex_lock(&mutex);
+    netflow_run__(nf);
+    ovs_mutex_unlock(&mutex);
+}
+
+void
+netflow_wait(struct netflow *nf) OVS_EXCLUDED(mutex)
 {
+    ovs_mutex_lock(&mutex);
     if (nf->active_timeout) {
         poll_timer_wait_until(nf->next_timeout);
     }
     if (nf->packet.size) {
         poll_immediate_wake();
     }
+    ovs_mutex_unlock(&mutex);
 }
 
 int
 netflow_set_options(struct netflow *nf,
                     const struct netflow_options *nf_options)
+    OVS_EXCLUDED(mutex)
 {
     int error = 0;
     long long int old_timeout;
 
+    ovs_mutex_lock(&mutex);
     nf->engine_type = nf_options->engine_type;
     nf->engine_id = nf_options->engine_id;
     nf->add_id_to_iface = nf_options->add_id_to_iface;
@@ -240,6 +387,7 @@ netflow_set_options(struct netflow *nf,
         nf->reconfig_time = time_msec();
         nf->next_timeout = time_msec();
     }
+    ovs_mutex_unlock(&mutex);
 
     return error;
 }
@@ -248,71 +396,98 @@ struct netflow *
 netflow_create(void)
 {
     struct netflow *nf = xzalloc(sizeof *nf);
+    int junk;
+
     nf->engine_type = 0;
     nf->engine_id = 0;
     nf->boot_time = time_msec();
     nf->collectors = NULL;
     nf->add_id_to_iface = false;
     nf->netflow_cnt = 0;
+    hmap_init(&nf->flows);
+    atomic_init(&nf->ref_cnt, 1);
     ofpbuf_init(&nf->packet, 1500);
+    atomic_add(&netflow_count, 1, &junk);
     return nf;
 }
 
-void
-netflow_destroy(struct netflow *nf)
+struct netflow *
+netflow_ref(const struct netflow *nf_)
 {
+    struct netflow *nf = CONST_CAST(struct netflow *, nf_);
     if (nf) {
-        ofpbuf_uninit(&nf->packet);
-        collectors_destroy(nf->collectors);
-        free(nf);
+        int orig;
+        atomic_add(&nf->ref_cnt, 1, &orig);
+        ovs_assert(orig > 0);
     }
+    return nf;
 }
 
-/* Initializes a new 'nf_flow' given that the caller has already cleared it to
- * all-zero-bits. */
 void
-netflow_flow_init(struct netflow_flow *nf_flow OVS_UNUSED)
+netflow_unref(struct netflow *nf)
 {
-    /* Nothing to do. */
+    int orig;
+
+    if (!nf) {
+        return;
+    }
+
+    atomic_sub(&nf->ref_cnt, 1, &orig);
+    ovs_assert(orig > 0);
+    if (orig == 1) {
+        atomic_sub(&netflow_count, 1, &orig);
+        collectors_destroy(nf->collectors);
+        ofpbuf_uninit(&nf->packet);
+        free(nf);
+    }
 }
 
-void
-netflow_flow_clear(struct netflow_flow *nf_flow)
+/* Returns true if there exist any netflow objects, false otherwise. */
+bool
+netflow_exists(void)
 {
-    ofp_port_t output_iface = nf_flow->output_iface;
+    int n;
 
-    memset(nf_flow, 0, sizeof *nf_flow);
-    nf_flow->output_iface = output_iface;
+    atomic_read(&netflow_count, &n);
+    return n > 0;
 }
+\f
+/* Helpers. */
 
-void
-netflow_flow_update_time(struct netflow *nf, struct netflow_flow *nf_flow,
-                         long long int used)
+static struct netflow_flow *
+netflow_flow_lookup(const struct netflow *nf, const struct flow *flow)
+    OVS_REQUIRES(mutex)
 {
-    if (!nf_flow->created) {
-        nf_flow->created = used;
+    struct netflow_flow *nf_flow;
+
+    HMAP_FOR_EACH_WITH_HASH (nf_flow, hmap_node, netflow_flow_hash(flow),
+                             &nf->flows) {
+        if (flow->in_port.ofp_port == nf_flow->in_port
+            && flow->nw_src == nf_flow->nw_src
+            && flow->nw_dst == nf_flow->nw_dst
+            && flow->nw_tos == nf_flow->nw_tos
+            && flow->nw_proto == nf_flow->nw_proto
+            && flow->tp_src == nf_flow->tp_src
+            && flow->tp_dst == nf_flow->tp_dst) {
+            return nf_flow;
+        }
     }
 
-    if (!nf || !nf->active_timeout || !nf_flow->last_expired ||
-        nf->reconfig_time > nf_flow->last_expired) {
-        /* Keep the time updated to prevent a flood of expiration in
-         * the future. */
-        nf_flow->last_expired = time_msec();
-    }
+    return NULL;
 }
 
-void
-netflow_flow_update_flags(struct netflow_flow *nf_flow, uint16_t tcp_flags)
+static uint32_t
+netflow_flow_hash(const struct flow *flow)
 {
-    nf_flow->tcp_flags |= tcp_flags;
-}
+    uint32_t hash = 0;
 
-bool
-netflow_active_timeout_expired(struct netflow *nf, struct netflow_flow *nf_flow)
-{
-    if (nf->active_timeout) {
-        return time_msec() > nf_flow->last_expired + nf->active_timeout;
-    }
+    hash = mhash_add(hash, (OVS_FORCE uint32_t) flow->in_port.ofp_port);
+    hash = mhash_add(hash, ntohl(flow->nw_src));
+    hash = mhash_add(hash, ntohl(flow->nw_dst));
+    hash = mhash_add(hash, flow->nw_tos);
+    hash = mhash_add(hash, flow->nw_proto);
+    hash = mhash_add(hash, ntohs(flow->tp_src));
+    hash = mhash_add(hash, ntohs(flow->tp_dst));
 
-    return false;
+    return mhash_finish(hash, 28);
 }
index e1a2443..c7f2574 100644 (file)
@@ -28,8 +28,6 @@
  * accounted.) */
 #define NF_ACTIVE_TIMEOUT_DEFAULT 600
 
-struct ofexpired;
-
 struct netflow_options {
     struct sset collectors;
     uint8_t engine_type;
@@ -42,33 +40,23 @@ struct netflow_options {
 #define NF_OUT_MULTI OFP_PORT_C(UINT16_MAX - 1)
 #define NF_OUT_DROP  OFP_PORT_C(UINT16_MAX - 2)
 
-struct netflow_flow {
-    long long int last_expired;   /* Time this flow last timed out. */
-    long long int created;        /* Time flow was created since time out. */
-
-    uint64_t packet_count_off;    /* Packet count at last time out. */
-    uint64_t byte_count_off;      /* Byte count at last time out. */
-
-    ofp_port_t output_iface;      /* Output interface index. */
-    uint16_t tcp_flags;           /* Bitwise-OR of all TCP flags seen. */
-};
-
 struct netflow *netflow_create(void);
-void netflow_destroy(struct netflow *);
+struct netflow *netflow_ref(const struct netflow *);
+void netflow_unref(struct netflow *);
+bool netflow_exists(void);
+
 int netflow_set_options(struct netflow *, const struct netflow_options *);
-void netflow_expire(struct netflow *, struct netflow_flow *,
-                    struct ofexpired *);
+void netflow_expire(struct netflow *, struct flow *);
 
-bool netflow_run(struct netflow *);
+void netflow_run(struct netflow *);
 void netflow_wait(struct netflow *);
 
 void netflow_mask_wc(struct flow *, struct flow_wildcards *);
 
-void netflow_flow_init(struct netflow_flow *);
-void netflow_flow_clear(struct netflow_flow *);
-void netflow_flow_update_time(struct netflow *, struct netflow_flow *,
-                              long long int used);
-void netflow_flow_update_flags(struct netflow_flow *, uint16_t tcp_flags);
-bool netflow_active_timeout_expired(struct netflow *, struct netflow_flow *);
+void netflow_flow_clear(struct netflow *netflow, struct flow *flow);
+
+void netflow_flow_update(struct netflow *nf, struct flow *flow,
+                         ofp_port_t output_iface,
+                         const struct dpif_flow_stats *);
 
 #endif /* netflow.h */
diff --git a/ofproto/ofproto-dpif-governor.c b/ofproto/ofproto-dpif-governor.c
deleted file mode 100644 (file)
index 459f871..0000000
+++ /dev/null
@@ -1,211 +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.
- */
-
-#include <config.h>
-
-#include "ofproto-dpif-governor.h"
-
-#include <stdlib.h>
-
-#include "coverage.h"
-#include "poll-loop.h"
-#include "random.h"
-#include "timeval.h"
-#include "util.h"
-#include "valgrind.h"
-#include "vlog.h"
-
-VLOG_DEFINE_THIS_MODULE(ofproto_dpif_governor);
-
-/* Minimum number of observed packets before setting up a flow.
- *
- * This value seems OK empirically. */
-#define FLOW_SETUP_THRESHOLD 5
-BUILD_ASSERT_DECL(FLOW_SETUP_THRESHOLD > 1);
-BUILD_ASSERT_DECL(FLOW_SETUP_THRESHOLD < 16);
-
-/* Minimum and maximum size of a governor, in bytes. */
-enum { MIN_SIZE = 16 * 1024 };
-enum { MAX_SIZE = 256 * 1024 };
-BUILD_ASSERT_DECL(IS_POW2(MIN_SIZE));
-BUILD_ASSERT_DECL(IS_POW2(MAX_SIZE));
-
-/* Minimum and maximum time to process the number of packets that make up a
- * given generation.  If a generation completes faster than the minimum time,
- * we double the table size (but no more than MAX_SIZE).  If a generation take
- * more than the maximum time to complete, we halve the table size (but no
- * smaller than MIN_SIZE). */
-enum { MIN_ELAPSED = 1000 }; /* In milliseconds. */
-enum { MAX_ELAPSED = 5000 }; /* In milliseconds. */
-
-static void governor_new_generation(struct governor *, unsigned int size);
-
-/* Creates and returns a new governor. */
-struct governor *
-governor_create(void)
-{
-    struct governor *g = xzalloc(sizeof *g);
-    governor_new_generation(g, MIN_SIZE);
-    return g;
-}
-
-/* Destroys 'g'. */
-void
-governor_destroy(struct governor *g)
-{
-    if (g) {
-        VLOG_INFO("disengaging");
-        free(g->table);
-        free(g);
-    }
-}
-
-/* Performs periodic maintenance work on 'g'. */
-void
-governor_run(struct governor *g)
-{
-    if (time_msec() - g->start > MAX_ELAPSED) {
-        if (g->size > MIN_SIZE) {
-            governor_new_generation(g, g->size / 2);
-        } else {
-            /* Don't start a new generation (we'd never go idle). */
-        }
-    }
-}
-
-/* Arranges for the poll loop to wake up when 'g' needs to do some work. */
-void
-governor_wait(struct governor *g)
-{
-    if (g->size > MIN_SIZE) {
-        poll_timer_wait_until(g->start + MAX_ELAPSED);
-    }
-}
-
-/* Returns true if 'g' has been doing only a minimal amount of work and thus
- * the client should consider getting rid of it entirely.  */
-bool
-governor_is_idle(struct governor *g)
-{
-    return g->size == MIN_SIZE && time_msec() - g->start > MAX_ELAPSED;
-}
-
-/* Tests whether a flow whose hash is 'hash' and for which 'n' packets have
- * just arrived should be set up in the datapath or just processed on a
- * packet-by-packet basis.  Returns true to set up a datapath flow, false to
- * process the packets individually.
- *
- * One would expect 'n' to ordinarily be 1, if batching leads multiple packets
- * to be processed at a time then it could be greater. */
-bool
-governor_should_install_flow(struct governor *g, uint32_t hash, int n)
-{
-    int old_count, new_count;
-    bool install_flow;
-    uint8_t *e;
-
-    ovs_assert(n > 0);
-
-    /* Count these packets and begin a new generation if necessary. */
-    g->n_packets += n;
-    if (g->n_packets >= g->size / 4) {
-        unsigned int new_size;
-        long long elapsed;
-
-        elapsed = time_msec() - g->start;
-        new_size = (elapsed < MIN_ELAPSED && g->size < MAX_SIZE ? g->size * 2
-                    : elapsed > MAX_ELAPSED && g->size > MIN_SIZE ? g->size / 2
-                    : g->size);
-        governor_new_generation(g, new_size);
-    }
-
-    /* If we've set up most of the flows we've seen, then we're wasting time
-     * handling most packets one at a time, so in this case instead set up most
-     * flows directly and use the remaining flows as a sample set to adjust our
-     * criteria later.
-     *
-     * The definition of "most" is conservative, but the sample size is tuned
-     * based on a few experiments with TCP_CRR mode in netperf. */
-    if (g->n_setups >= g->n_flows - g->n_flows / 16
-        && g->n_flows >= 64
-        && hash & 0x3f) {
-        g->n_shortcuts++;
-        return true;
-    }
-
-    /* Do hash table processing.
-     *
-     * Even-numbered hash values use high-order nibbles.
-     * Odd-numbered hash values use low-order nibbles. */
-    e = &g->table[(hash >> 1) & (g->size - 1)];
-    old_count = (hash & 1 ? *e >> 4 : *e & 0x0f);
-    if (!old_count) {
-        g->n_flows++;
-    }
-    new_count = n + old_count;
-    if (new_count >= FLOW_SETUP_THRESHOLD) {
-        g->n_setups++;
-        install_flow = true;
-        new_count = 0;
-    } else {
-        install_flow = false;
-    }
-    *e = hash & 1 ? (new_count << 4) | (*e & 0x0f) : (*e & 0xf0) | new_count;
-
-    return install_flow;
-}
-\f
-/* Starts a new generation in 'g' with a table size of 'size' bytes.  'size'
- * must be a power of two between MIN_SIZE and MAX_SIZE, inclusive. */
-static void
-governor_new_generation(struct governor *g, unsigned int size)
-{
-    ovs_assert(size >= MIN_SIZE && size <= MAX_SIZE);
-    ovs_assert(is_pow2(size));
-
-    /* Allocate new table, if necessary. */
-    if (g->size != size) {
-        if (!g->size) {
-            VLOG_INFO("engaging governor with %u kB hash table", size / 1024);
-        } else {
-            VLOG_INFO("processed %u packets in %.2f s, "
-                      "%s hash table to %u kB "
-                      "(%u hashes, %u setups, %u shortcuts)",
-                      g->n_packets,
-                      (time_msec() - g->start) / 1000.0,
-                      size > g->size ? "enlarging" : "shrinking",
-                      size / 1024,
-                      g->n_flows, g->n_setups, g->n_shortcuts);
-        }
-
-        free(g->table);
-        g->table = xmalloc(size * sizeof *g->table);
-        g->size = size;
-    } else {
-        VLOG_DBG("processed %u packets in %.2f s with %u kB hash table "
-                 "(%u hashes, %u setups, %u shortcuts)",
-                 g->n_packets, (time_msec() - g->start) / 1000.0,
-                 size / 1024, g->n_flows, g->n_setups, g->n_shortcuts);
-    }
-
-    /* Clear data for next generation. */
-    memset(g->table, 0, size * sizeof *g->table);
-    g->start = time_msec();
-    g->n_packets = 0;
-    g->n_flows /= 2;
-    g->n_setups /= 2;
-    g->n_shortcuts = 0;
-}
diff --git a/ofproto/ofproto-dpif-governor.h b/ofproto/ofproto-dpif-governor.h
deleted file mode 100644 (file)
index 7e6ec92..0000000
+++ /dev/null
@@ -1,58 +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 OFPROTO_DPIF_GOVERNOR_H
-#define OFPROTO_DPIF_GOVERNOR_H 1
-
-/* Flow setup rate limiter.
- *
- * A governor in an engine limits a vehicle's speed.  This governor limits the
- * rate at which flows are set up in the datapath.  The client provides as
- * input the hashes of observed packets.  The governor keeps track of hashes
- * seen multiple times.  When a given hash is seen often enough, the governor
- * indicates to its client that it should set up a facet and a subfacet and a
- * datapath flow for that flow.
- *
- * The same tracking could be done in terms of facets and subfacets directly,
- * but the governor code uses much less time and space to do the same job. */
-
-#include <stdbool.h>
-#include <stdint.h>
-
-struct governor {
-    char *name;                 /* Name, for log messages. */
-    uint8_t *table;             /* Table of counters, two per byte. */
-    unsigned int size;          /* Table size in bytes. */
-    long long int start;        /* Time when the table was last cleared. */
-    unsigned int n_packets;     /* Number of packets processed. */
-
-    /* Statistics for skipping counters when most flows get set up. */
-    unsigned int n_flows;       /* Number of unique flows seen. */
-    unsigned int n_setups;      /* Number of flows set up based on counters. */
-    unsigned int n_shortcuts;   /* Number of flows set up based on history. */
-};
-
-struct governor *governor_create(void);
-void governor_destroy(struct governor *);
-
-void governor_run(struct governor *);
-void governor_wait(struct governor *);
-
-bool governor_is_idle(struct governor *);
-
-bool governor_should_install_flow(struct governor *, uint32_t hash, int n);
-
-#endif /* ofproto/ofproto-dpif-governor.h */
index 0819b72..99172dd 100644 (file)
@@ -75,7 +75,6 @@ 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)
@@ -363,7 +362,7 @@ mirror_update_stats(struct mbridge *mbridge, mirror_mask_t mirrors,
     for (; mirrors; mirrors = zero_rightmost_1bit(mirrors)) {
         struct mirror *m;
 
-        m = mbridge->mirrors[mirror_mask_ffs(mirrors) - 1];
+        m = mbridge->mirrors[raw_ctz(mirrors)];
 
         if (!m) {
             /* In normal circumstances 'm' will not be NULL.  However,
index 4a6f3ce..64c4561 100644 (file)
@@ -52,10 +52,4 @@ 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 d06b2e1..33115f3 100644 (file)
@@ -53,7 +53,7 @@ struct mport {
 };
 
 /* hmap that contains "struct mport"s. */
-static struct hmap monitor_hmap;
+static struct hmap monitor_hmap = HMAP_INITIALIZER(&monitor_hmap);
 
 /* heap for ordering mport based on bfd/cfm wakeup time. */
 static struct heap monitor_heap;
@@ -63,11 +63,9 @@ static pthread_t monitor_tid;
 /* True if the monitor thread is running. */
 static bool monitor_running;
 
-static struct seq *monitor_seq;
 static struct latch monitor_exit_latch;
 static struct ovs_rwlock monitor_rwlock = OVS_RWLOCK_INITIALIZER;
 
-static void monitor_init(void);
 static void *monitor_main(void *);
 static void monitor_run(void);
 
@@ -149,27 +147,13 @@ mport_update(struct mport *mport, struct bfd *bfd, struct cfm *cfm,
         memcpy(mport->hw_addr, hw_addr, ETH_ADDR_LEN);
     }
     /* If bfd/cfm is added or reconfigured, move the mport on top of the heap
-     * and wakes up the monitor thread. */
+     * so that the monitor thread can run the mport next time it wakes up. */
     if (mport->bfd || mport->cfm) {
         heap_change(&monitor_heap, &mport->heap_node, LLONG_MAX);
-        seq_change(monitor_seq);
     }
 }
 \f
 
-/* Initializes the global variables.  This will only run once. */
-static void
-monitor_init(void)
-{
-    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
-
-    if (ovsthread_once_start(&once)) {
-        hmap_init(&monitor_hmap);
-        monitor_seq = seq_create();
-        ovsthread_once_done(&once);
-    }
-}
-
 /* The 'main' function for the monitor thread. */
 static void *
 monitor_main(void * args OVS_UNUSED)
@@ -177,17 +161,18 @@ monitor_main(void * args OVS_UNUSED)
     set_subprogram_name("monitor");
     VLOG_INFO("monitor thread created");
     while (!latch_is_set(&monitor_exit_latch)) {
-        uint64_t seq = seq_read(monitor_seq);
-
         monitor_run();
         latch_wait(&monitor_exit_latch);
-        seq_wait(monitor_seq, seq);
         poll_block();
     }
     VLOG_INFO("monitor thread terminated");
     return NULL;
 }
 
+/* The monitor thread should wake up this often to ensure that newly added or
+ * reconfigured monitoring ports are run in a timely manner. */
+#define MONITOR_INTERVAL_MSEC 100
+
 /* Checks the sending of control packets on mports that have timed out.
  * Sends the control packets if needed.  Executes bfd and cfm periodic
  * functions (run, wait) on those mports. */
@@ -199,7 +184,7 @@ monitor_run(void)
     struct ofpbuf packet;
 
     ofpbuf_use_stub(&packet, stub, sizeof stub);
-    ovs_rwlock_rdlock(&monitor_rwlock);
+    ovs_rwlock_wrlock(&monitor_rwlock);
     prio_now = MSEC_TO_PRIO(time_msec());
     /* Peeks the top of heap and checks if we should run this mport. */
     while (!heap_is_empty(&monitor_heap)
@@ -227,14 +212,19 @@ monitor_run(void)
             bfd_wait(mport->bfd);
         }
         /* Computes the next wakeup time for this mport. */
-        next_wake_time = MIN(bfd_wake_time(mport->bfd), cfm_wake_time(mport->cfm));
-        heap_change(&monitor_heap, heap_max(&monitor_heap),
+        next_wake_time = MIN(bfd_wake_time(mport->bfd),
+                             cfm_wake_time(mport->cfm));
+        heap_change(&monitor_heap, &mport->heap_node,
                     MSEC_TO_PRIO(next_wake_time));
     }
 
     /* Waits on the earliest next wakeup time. */
     if (!heap_is_empty(&monitor_heap)) {
-        poll_timer_wait_until(PRIO_TO_MSEC(heap_max(&monitor_heap)->priority));
+        long long int next_timeout, next_mport_wakeup;
+
+        next_timeout = time_msec() + MONITOR_INTERVAL_MSEC;
+        next_mport_wakeup = PRIO_TO_MSEC(heap_max(&monitor_heap)->priority);
+        poll_timer_wait_until(MIN(next_timeout, next_mport_wakeup));
     }
     ovs_rwlock_unlock(&monitor_rwlock);
     ofpbuf_uninit(&packet);
@@ -250,7 +240,6 @@ ofproto_dpif_monitor_port_update(const struct ofport_dpif *ofport,
                                  struct bfd *bfd, struct cfm *cfm,
                                  uint8_t hw_addr[ETH_ADDR_LEN])
 {
-    monitor_init();
     ovs_rwlock_wrlock(&monitor_rwlock);
     if (!cfm && !bfd) {
         mport_unregister(ofport);
@@ -273,3 +262,26 @@ ofproto_dpif_monitor_port_update(const struct ofport_dpif *ofport,
         monitor_running = false;
     }
 }
+
+/* Moves the mport on top of the heap.  This is necessary when
+ * for example, bfd POLL is received and the mport should
+ * immediately send FINAL back. */
+void
+ofproto_dpif_monitor_port_send_soon_safe(const struct ofport_dpif *ofport)
+{
+    ovs_rwlock_wrlock(&monitor_rwlock);
+    ofproto_dpif_monitor_port_send_soon(ofport);
+    ovs_rwlock_unlock(&monitor_rwlock);
+}
+
+void
+ofproto_dpif_monitor_port_send_soon(const struct ofport_dpif *ofport)
+    OVS_REQ_WRLOCK(monitor_rwlock)
+{
+    struct mport *mport;
+
+    mport = mport_find(ofport);
+    if (mport) {
+        heap_change(&monitor_heap, &mport->heap_node, LLONG_MAX);
+    }
+}
index f914fbe..1f6be5c 100644 (file)
@@ -23,6 +23,9 @@ struct bfd;
 struct cfm;
 struct ofport_dpif;
 
+void ofproto_dpif_monitor_port_send_soon(const struct ofport_dpif *);
+void ofproto_dpif_monitor_port_send_soon_safe(const struct ofport_dpif *);
+
 void ofproto_dpif_monitor_port_update(const struct ofport_dpif *,
                                       struct bfd *, struct cfm *,
                                       uint8_t[OFP_ETH_ALEN]);
index 0512b21..14e9ff2 100644 (file)
@@ -11,9 +11,10 @@ list of connected ports.  The port information includes the OpenFlow
 port number, datapath port number, and the type.  (The local port is
 identified as OpenFlow port 65534.)
 .
-.IP "\fBdpif/dump\-flows \fIdp\fR"
+.IP "\fBdpif/dump\-flows\fR [\fB\-m\fR] \fIdp\fR"
 Prints to the console all flow entries in datapath \fIdp\fR's
-flow table.
+flow table. Without \fB\-m\fR, output omits match fields that a flow
+wildcards entirely; with \fB\-m\fR output includes all wildcarded fields.
 .IP
 This command is primarily useful for debugging Open vSwitch.  The flow
 table entries that it displays are not OpenFlow flow entries.  Instead,
index dde6430..b57afdc 100644 (file)
 
 #include "connmgr.h"
 #include "coverage.h"
-#include "dynamic-string.h"
 #include "dpif.h"
+#include "dynamic-string.h"
 #include "fail-open.h"
 #include "guarded-list.h"
 #include "latch.h"
-#include "seq.h"
 #include "list.h"
 #include "netlink.h"
 #include "ofpbuf.h"
 #include "ofproto-dpif-ipfix.h"
 #include "ofproto-dpif-sflow.h"
+#include "ofproto-dpif-xlate.h"
 #include "packets.h"
 #include "poll-loop.h"
+#include "seq.h"
+#include "unixctl.h"
 #include "vlog.h"
 
 #define MAX_QUEUE_LENGTH 512
+#define FLOW_MISS_MAX_BATCH 50
+#define REVALIDATE_MAX_BATCH 50
 
 VLOG_DEFINE_THIS_MODULE(ofproto_dpif_upcall);
 
-COVERAGE_DEFINE(drop_queue_overflow);
 COVERAGE_DEFINE(upcall_queue_overflow);
-COVERAGE_DEFINE(fmb_queue_overflow);
-COVERAGE_DEFINE(fmb_queue_revalidated);
 
 /* A thread that processes each upcall handed to it by the dispatcher thread,
- * forwards the upcall's packet, and then queues it to the main ofproto_dpif
- * to possibly set up a kernel flow as a cache. */
+ * forwards the upcall's packet, and possibly sets up a kernel flow as a
+ * cache. */
 struct handler {
     struct udpif *udpif;               /* Parent udpif. */
     pthread_t thread;                  /* Thread ID. */
+    char *name;                        /* Thread name. */
 
     struct ovs_mutex mutex;            /* Mutex guarding the following. */
 
@@ -58,41 +60,73 @@ struct handler {
     struct list upcalls OVS_GUARDED;
     size_t n_upcalls OVS_GUARDED;
 
-    size_t n_new_upcalls;              /* Only changed by the dispatcher. */
     bool need_signal;                  /* Only changed by the dispatcher. */
 
     pthread_cond_t wake_cond;          /* Wakes 'thread' while holding
                                           'mutex'. */
 };
 
+/* A thread that processes each kernel flow handed to it by the flow_dumper
+ * thread, updates OpenFlow statistics, and updates or removes the kernel flow
+ * as necessary. */
+struct revalidator {
+    struct udpif *udpif;               /* Parent udpif. */
+    char *name;                        /* Thread name. */
+
+    pthread_t thread;                  /* Thread ID. */
+    struct hmap ukeys;                 /* Datapath flow keys. */
+
+    uint64_t dump_seq;
+
+    struct ovs_mutex mutex;            /* Mutex guarding the following. */
+    pthread_cond_t wake_cond;
+    struct list udumps OVS_GUARDED;    /* Unprocessed udumps. */
+    size_t n_udumps OVS_GUARDED;       /* Number of unprocessed udumps. */
+};
+
 /* An upcall handler for ofproto_dpif.
  *
- * udpif is implemented as a "dispatcher" thread that reads upcalls from the
- * kernel.  It processes each upcall just enough to figure out its next
- * destination.  For a "miss" upcall (MISS_UPCALL), this is one of several
- * "handler" threads (see struct handler).  Other upcalls are queued to the
- * main ofproto_dpif. */
+ * udpif has two logically separate pieces:
+ *
+ *    - A "dispatcher" thread that reads upcalls from the kernel and dispatches
+ *      them to one of several "handler" threads (see struct handler).
+ *
+ *    - A "flow_dumper" thread that reads the kernel flow table and dispatches
+ *      flows to one of several "revalidator" threads (see struct
+ *      revalidator). */
 struct udpif {
+    struct list list_node;             /* In all_udpifs list. */
+
     struct dpif *dpif;                 /* Datapath handle. */
     struct dpif_backer *backer;        /* Opaque dpif_backer pointer. */
 
     uint32_t secret;                   /* Random seed for upcall hash. */
 
     pthread_t dispatcher;              /* Dispatcher thread ID. */
+    pthread_t flow_dumper;             /* Flow dumper thread ID. */
 
     struct handler *handlers;          /* Upcall handlers. */
     size_t n_handlers;
 
-    /* Queues to pass up to ofproto-dpif. */
-    struct guarded_list drop_keys; /* "struct drop key"s. */
-    struct guarded_list fmbs;      /* "struct flow_miss_batch"es. */
+    struct revalidator *revalidators;  /* Flow revalidators. */
+    size_t n_revalidators;
 
-    /* Number of times udpif_revalidate() has been called. */
-    atomic_uint reval_seq;
+    uint64_t last_reval_seq;           /* 'reval_seq' at last revalidation. */
+    struct seq *reval_seq;             /* Incremented to force revalidation. */
 
-    struct seq *wait_seq;
+    struct seq *dump_seq;              /* Increments each dump iteration. */
 
-    struct latch exit_latch; /* Tells child threads to exit. */
+    struct latch exit_latch;           /* Tells child threads to exit. */
+
+    long long int dump_duration;       /* Duration of the last flow dump. */
+
+    /* Datapath flow statistics. */
+    unsigned int max_n_flows;
+    unsigned int avg_n_flows;
+
+    /* Following fields are accessed and modified by different threads. */
+    atomic_llong max_idle;             /* Maximum datapath flow idle time. */
+    atomic_uint flow_limit;            /* Datapath flow hard limit. */
 };
 
 enum upcall_type {
@@ -113,29 +147,118 @@ struct upcall {
     uint64_t upcall_stub[512 / 8];  /* Buffer to reduce need for malloc(). */
 };
 
+/* 'udpif_key's are responsible for tracking the little bit of state udpif
+ * needs to do flow expiration which can't be pulled directly from the
+ * datapath.  They are owned, created by, maintained, and destroyed by a single
+ * revalidator making them easy to efficiently handle with multiple threads. */
+struct udpif_key {
+    struct hmap_node hmap_node;     /* In parent revalidator 'ukeys' map. */
+
+    struct nlattr *key;            /* Datapath flow key. */
+    size_t key_len;                /* Length of 'key'. */
+
+    struct dpif_flow_stats stats;  /* Stats at most recent flow dump. */
+    long long int created;         /* Estimation of creation time. */
+
+    bool mark;                     /* Used by mark and sweep GC algorithm. */
+
+    struct odputil_keybuf key_buf; /* Memory for 'key'. */
+};
+
+/* 'udpif_flow_dump's hold the state associated with one iteration in a flow
+ * dump operation.  This is created by the flow_dumper thread and handed to the
+ * appropriate revalidator thread to be processed. */
+struct udpif_flow_dump {
+    struct list list_node;
+
+    struct nlattr *key;            /* Datapath flow key. */
+    size_t key_len;                /* Length of 'key'. */
+    uint32_t key_hash;             /* Hash of 'key'. */
+
+    struct odputil_keybuf mask_buf;
+    struct nlattr *mask;           /* Datapath mask for 'key'. */
+    size_t mask_len;               /* Length of 'mask'. */
+
+    struct dpif_flow_stats stats;  /* Stats pulled from the datapath. */
+
+    bool need_revalidate;          /* Key needs revalidation? */
+
+    struct odputil_keybuf key_buf;
+};
+
+/* Flow miss batching.
+ *
+ * Some dpifs implement operations faster when you hand them off in a batch.
+ * To allow batching, "struct flow_miss" queues the dpif-related work needed
+ * for a given flow.  Each "struct flow_miss" corresponds to sending one or
+ * more packets, plus possibly installing the flow in the dpif. */
+struct flow_miss {
+    struct hmap_node hmap_node;
+    struct ofproto_dpif *ofproto;
+
+    struct flow flow;
+    enum odp_key_fitness key_fitness;
+    const struct nlattr *key;
+    size_t key_len;
+    enum dpif_upcall_type upcall_type;
+    struct dpif_flow_stats stats;
+    odp_port_t odp_in_port;
+
+    uint64_t slow_path_buf[128 / 8];
+    struct odputil_keybuf mask_buf;
+
+    struct xlate_out xout;
+};
+
 static void upcall_destroy(struct upcall *);
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+static struct list all_udpifs = LIST_INITIALIZER(&all_udpifs);
 
 static void recv_upcalls(struct udpif *);
-static void handle_upcalls(struct udpif *, struct list *upcalls);
-static void miss_destroy(struct flow_miss *);
+static void handle_upcalls(struct handler *handler, struct list *upcalls);
+static void *udpif_flow_dumper(void *);
 static void *udpif_dispatcher(void *);
 static void *udpif_upcall_handler(void *);
+static void *udpif_revalidator(void *);
+static uint64_t udpif_get_n_flows(const struct udpif *);
+static void revalidate_udumps(struct revalidator *, struct list *udumps);
+static void revalidator_sweep(struct revalidator *);
+static void upcall_unixctl_show(struct unixctl_conn *conn, int argc,
+                                const char *argv[], void *aux);
+static void upcall_unixctl_disable_megaflows(struct unixctl_conn *, int argc,
+                                             const char *argv[], void *aux);
+static void upcall_unixctl_enable_megaflows(struct unixctl_conn *, int argc,
+                                            const char *argv[], void *aux);
+static void ukey_delete(struct revalidator *, struct udpif_key *);
+
+static atomic_bool enable_megaflows = ATOMIC_VAR_INIT(true);
 
 struct udpif *
 udpif_create(struct dpif_backer *backer, struct dpif *dpif)
 {
+    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
     struct udpif *udpif = xzalloc(sizeof *udpif);
 
+    if (ovsthread_once_start(&once)) {
+        unixctl_command_register("upcall/show", "", 0, 0, upcall_unixctl_show,
+                                 NULL);
+        unixctl_command_register("upcall/disable-megaflows", "", 0, 0,
+                                 upcall_unixctl_disable_megaflows, NULL);
+        unixctl_command_register("upcall/enable-megaflows", "", 0, 0,
+                                 upcall_unixctl_enable_megaflows, NULL);
+        ovsthread_once_done(&once);
+    }
+
     udpif->dpif = dpif;
     udpif->backer = backer;
+    atomic_init(&udpif->max_idle, 5000);
+    atomic_init(&udpif->flow_limit, MIN(ofproto_flow_limit, 10000));
     udpif->secret = random_uint32();
-    udpif->wait_seq = seq_create();
+    udpif->reval_seq = seq_create();
+    udpif->dump_seq = seq_create();
     latch_init(&udpif->exit_latch);
-    guarded_list_init(&udpif->drop_keys);
-    guarded_list_init(&udpif->fmbs);
-    atomic_init(&udpif->reval_seq, 0);
+    list_push_back(&all_udpifs, &udpif->list_node);
 
     return udpif;
 }
@@ -143,70 +266,93 @@ udpif_create(struct dpif_backer *backer, struct dpif *dpif)
 void
 udpif_destroy(struct udpif *udpif)
 {
-    struct flow_miss_batch *fmb;
-    struct drop_key *drop_key;
-
-    udpif_recv_set(udpif, 0, false);
-
-    while ((drop_key = drop_key_next(udpif))) {
-        drop_key_destroy(drop_key);
-    }
-
-    while ((fmb = flow_miss_batch_next(udpif))) {
-        flow_miss_batch_destroy(fmb);
-    }
+    udpif_set_threads(udpif, 0, 0);
+    udpif_flush();
 
-    guarded_list_destroy(&udpif->drop_keys);
-    guarded_list_destroy(&udpif->fmbs);
+    list_remove(&udpif->list_node);
     latch_destroy(&udpif->exit_latch);
-    seq_destroy(udpif->wait_seq);
+    seq_destroy(udpif->reval_seq);
+    seq_destroy(udpif->dump_seq);
     free(udpif);
 }
 
-/* Tells 'udpif' to begin or stop handling flow misses depending on the value
- * of 'enable'.  'n_handlers' is the number of upcall_handler threads to
- * create.  Passing 'n_handlers' as zero is equivalent to passing 'enable' as
- * false. */
+/* Tells 'udpif' how many threads it should use to handle upcalls.  Disables
+ * all threads if 'n_handlers' and 'n_revalidators' is zero.  'udpif''s
+ * datapath handle must have packet reception enabled before starting threads.
+ */
 void
-udpif_recv_set(struct udpif *udpif, size_t n_handlers, bool enable)
+udpif_set_threads(struct udpif *udpif, size_t n_handlers,
+                  size_t n_revalidators)
 {
-    n_handlers = enable ? n_handlers : 0;
-    n_handlers = MIN(n_handlers, 64);
-
     /* Stop the old threads (if any). */
-    if (udpif->handlers && udpif->n_handlers != n_handlers) {
+    if (udpif->handlers &&
+        (udpif->n_handlers != n_handlers
+         || udpif->n_revalidators != n_revalidators)) {
         size_t i;
 
         latch_set(&udpif->exit_latch);
 
-        /* Wake the handlers so they can exit. */
         for (i = 0; i < udpif->n_handlers; i++) {
             struct handler *handler = &udpif->handlers[i];
 
             ovs_mutex_lock(&handler->mutex);
             xpthread_cond_signal(&handler->wake_cond);
             ovs_mutex_unlock(&handler->mutex);
+            xpthread_join(handler->thread, NULL);
+        }
+
+        for (i = 0; i < udpif->n_revalidators; i++) {
+            struct revalidator *revalidator = &udpif->revalidators[i];
+
+            ovs_mutex_lock(&revalidator->mutex);
+            xpthread_cond_signal(&revalidator->wake_cond);
+            ovs_mutex_unlock(&revalidator->mutex);
+            xpthread_join(revalidator->thread, NULL);
         }
 
+        xpthread_join(udpif->flow_dumper, NULL);
         xpthread_join(udpif->dispatcher, NULL);
+
+        for (i = 0; i < udpif->n_revalidators; i++) {
+            struct revalidator *revalidator = &udpif->revalidators[i];
+            struct udpif_flow_dump *udump, *next_udump;
+            struct udpif_key *ukey, *next_ukey;
+
+            LIST_FOR_EACH_SAFE (udump, next_udump, list_node,
+                                &revalidator->udumps) {
+                list_remove(&udump->list_node);
+                free(udump);
+            }
+
+            HMAP_FOR_EACH_SAFE (ukey, next_ukey, hmap_node,
+                                &revalidator->ukeys) {
+                ukey_delete(revalidator, ukey);
+            }
+            hmap_destroy(&revalidator->ukeys);
+            ovs_mutex_destroy(&revalidator->mutex);
+
+            free(revalidator->name);
+        }
+
         for (i = 0; i < udpif->n_handlers; i++) {
             struct handler *handler = &udpif->handlers[i];
             struct upcall *miss, *next;
 
-            xpthread_join(handler->thread, NULL);
-
-            ovs_mutex_lock(&handler->mutex);
             LIST_FOR_EACH_SAFE (miss, next, list_node, &handler->upcalls) {
                 list_remove(&miss->list_node);
                 upcall_destroy(miss);
             }
-            ovs_mutex_unlock(&handler->mutex);
             ovs_mutex_destroy(&handler->mutex);
 
             xpthread_cond_destroy(&handler->wake_cond);
+            free(handler->name);
         }
         latch_poll(&udpif->exit_latch);
 
+        free(udpif->revalidators);
+        udpif->revalidators = NULL;
+        udpif->n_revalidators = 0;
+
         free(udpif->handlers);
         udpif->handlers = NULL;
         udpif->n_handlers = 0;
@@ -217,6 +363,8 @@ udpif_recv_set(struct udpif *udpif, size_t n_handlers, bool enable)
         size_t i;
 
         udpif->n_handlers = n_handlers;
+        udpif->n_revalidators = n_revalidators;
+
         udpif->handlers = xzalloc(udpif->n_handlers * sizeof *udpif->handlers);
         for (i = 0; i < udpif->n_handlers; i++) {
             struct handler *handler = &udpif->handlers[i];
@@ -229,19 +377,22 @@ udpif_recv_set(struct udpif *udpif, size_t n_handlers, bool enable)
             xpthread_create(&handler->thread, NULL, udpif_upcall_handler,
                             handler);
         }
-        xpthread_create(&udpif->dispatcher, NULL, udpif_dispatcher, udpif);
-    }
-}
 
-void
-udpif_wait(struct udpif *udpif)
-{
-    uint64_t seq = seq_read(udpif->wait_seq);
-    if (!guarded_list_is_empty(&udpif->drop_keys) ||
-        !guarded_list_is_empty(&udpif->fmbs)) {
-        poll_immediate_wake();
-    } else {
-        seq_wait(udpif->wait_seq, seq);
+        udpif->revalidators = xzalloc(udpif->n_revalidators
+                                      * sizeof *udpif->revalidators);
+        for (i = 0; i < udpif->n_revalidators; i++) {
+            struct revalidator *revalidator = &udpif->revalidators[i];
+
+            revalidator->udpif = udpif;
+            list_init(&revalidator->udumps);
+            hmap_init(&revalidator->ukeys);
+            ovs_mutex_init(&revalidator->mutex);
+            xpthread_cond_init(&revalidator->wake_cond, NULL);
+            xpthread_create(&revalidator->thread, NULL, udpif_revalidator,
+                            revalidator);
+        }
+        xpthread_create(&udpif->dispatcher, NULL, udpif_dispatcher, udpif);
+        xpthread_create(&udpif->flow_dumper, NULL, udpif_flow_dumper, udpif);
     }
 }
 
@@ -250,124 +401,79 @@ udpif_wait(struct udpif *udpif)
 void
 udpif_revalidate(struct udpif *udpif)
 {
-    struct flow_miss_batch *fmb, *next_fmb;
-    unsigned int junk;
-    struct list fmbs;
-
-    /* Since we remove each miss on revalidation, their statistics won't be
-     * accounted to the appropriate 'facet's in the upper layer.  In most
-     * cases, this is alright because we've already pushed the stats to the
-     * relevant rules.  However, NetFlow requires absolute packet counts on
-     * 'facet's which could now be incorrect. */
-    atomic_add(&udpif->reval_seq, 1, &junk);
-
-    guarded_list_pop_all(&udpif->fmbs, &fmbs);
-    LIST_FOR_EACH_SAFE (fmb, next_fmb, list_node, &fmbs) {
-        list_remove(&fmb->list_node);
-        flow_miss_batch_destroy(fmb);
-    }
-
-    udpif_drop_key_clear(udpif);
+    seq_change(udpif->reval_seq);
 }
 
-/* Destroys and deallocates 'upcall'. */
-static void
-upcall_destroy(struct upcall *upcall)
+/* Returns a seq which increments every time 'udpif' pulls stats from the
+ * datapath.  Callers can use this to get a sense of when might be a good time
+ * to do periodic work which relies on relatively up to date statistics. */
+struct seq *
+udpif_dump_seq(struct udpif *udpif)
 {
-    if (upcall) {
-        ofpbuf_uninit(&upcall->upcall_buf);
-        free(upcall);
-    }
+    return udpif->dump_seq;
 }
 
-/* Retrieves the next batch of processed flow misses for 'udpif' to install.
- * The caller is responsible for destroying it with flow_miss_batch_destroy().
- */
-struct flow_miss_batch *
-flow_miss_batch_next(struct udpif *udpif)
+void
+udpif_get_memory_usage(struct udpif *udpif, struct simap *usage)
 {
-    int i;
-
-    for (i = 0; i < 50; i++) {
-        struct flow_miss_batch *next;
-        unsigned int reval_seq;
-        struct list *next_node;
-
-        next_node = guarded_list_pop_front(&udpif->fmbs);
-        if (!next_node) {
-            break;
-        }
+    size_t i;
 
-        next = CONTAINER_OF(next_node, struct flow_miss_batch, list_node);
-        atomic_read(&udpif->reval_seq, &reval_seq);
-        if (next->reval_seq == reval_seq) {
-            return next;
-        }
+    simap_increase(usage, "dispatchers", 1);
+    simap_increase(usage, "flow_dumpers", 1);
 
-        flow_miss_batch_destroy(next);
+    simap_increase(usage, "handlers", udpif->n_handlers);
+    for (i = 0; i < udpif->n_handlers; i++) {
+        struct handler *handler = &udpif->handlers[i];
+        ovs_mutex_lock(&handler->mutex);
+        simap_increase(usage, "handler upcalls",  handler->n_upcalls);
+        ovs_mutex_unlock(&handler->mutex);
     }
 
-    return NULL;
+    simap_increase(usage, "revalidators", udpif->n_revalidators);
+    for (i = 0; i < udpif->n_revalidators; i++) {
+        struct revalidator *revalidator = &udpif->revalidators[i];
+        ovs_mutex_lock(&revalidator->mutex);
+        simap_increase(usage, "revalidator dumps", revalidator->n_udumps);
+
+        /* XXX: This isn't technically thread safe because the revalidator
+         * ukeys maps isn't protected by a mutex since it's per thread. */
+        simap_increase(usage, "revalidator keys",
+                       hmap_count(&revalidator->ukeys));
+        ovs_mutex_unlock(&revalidator->mutex);
+    }
 }
 
-/* Destroys and deallocates 'fmb'. */
+/* Removes all flows from all datapaths. */
 void
-flow_miss_batch_destroy(struct flow_miss_batch *fmb)
+udpif_flush(void)
 {
-    struct flow_miss *miss, *next;
-    struct upcall *upcall, *next_upcall;
-
-    if (!fmb) {
-        return;
-    }
-
-    HMAP_FOR_EACH_SAFE (miss, next, hmap_node, &fmb->misses) {
-        hmap_remove(&fmb->misses, &miss->hmap_node);
-        miss_destroy(miss);
-    }
+    struct udpif *udpif;
 
-    LIST_FOR_EACH_SAFE (upcall, next_upcall, list_node, &fmb->upcalls) {
-        list_remove(&upcall->list_node);
-        upcall_destroy(upcall);
+    LIST_FOR_EACH (udpif, list_node, &all_udpifs) {
+        dpif_flow_flush(udpif->dpif);
     }
-
-    hmap_destroy(&fmb->misses);
-    free(fmb);
-}
-
-/* Retrieves the next drop key which ofproto-dpif needs to process.  The caller
- * is responsible for destroying it with drop_key_destroy(). */
-struct drop_key *
-drop_key_next(struct udpif *udpif)
-{
-    struct list *next = guarded_list_pop_front(&udpif->drop_keys);
-    return next ? CONTAINER_OF(next, struct drop_key, list_node) : NULL;
 }
-
-/* Destroys and deallocates 'drop_key'. */
-void
-drop_key_destroy(struct drop_key *drop_key)
+\f
+/* Destroys and deallocates 'upcall'. */
+static void
+upcall_destroy(struct upcall *upcall)
 {
-    if (drop_key) {
-        free(drop_key->key);
-        free(drop_key);
+    if (upcall) {
+        ofpbuf_uninit(&upcall->dpif_upcall.packet);
+        ofpbuf_uninit(&upcall->upcall_buf);
+        free(upcall);
     }
 }
 
-/* Clears all drop keys waiting to be processed by drop_key_next(). */
-void
-udpif_drop_key_clear(struct udpif *udpif)
+static uint64_t
+udpif_get_n_flows(const struct udpif *udpif)
 {
-    struct drop_key *drop_key, *next;
-    struct list list;
+    struct dpif_dp_stats stats;
 
-    guarded_list_pop_all(&udpif->drop_keys, &list);
-    LIST_FOR_EACH_SAFE (drop_key, next, list_node, &list) {
-        list_remove(&drop_key->list_node);
-        drop_key_destroy(drop_key);
-    }
+    dpif_get_dp_stats(udpif->dpif, &stats);
+    return stats.n_flows;
 }
-\f
+
 /* The dispatcher thread is responsible for receiving upcalls from the kernel,
  * assigning them to a upcall_handler thread. */
 static void *
@@ -386,6 +492,128 @@ udpif_dispatcher(void *arg)
     return NULL;
 }
 
+static void *
+udpif_flow_dumper(void *arg)
+{
+    struct udpif *udpif = arg;
+
+    set_subprogram_name("flow_dumper");
+    while (!latch_is_set(&udpif->exit_latch)) {
+        const struct dpif_flow_stats *stats;
+        long long int start_time, duration;
+        const struct nlattr *key, *mask;
+        struct dpif_flow_dump dump;
+        size_t key_len, mask_len;
+        unsigned int flow_limit;
+        long long int max_idle;
+        bool need_revalidate;
+        uint64_t reval_seq;
+        size_t n_flows, i;
+
+        reval_seq = seq_read(udpif->reval_seq);
+        need_revalidate = udpif->last_reval_seq != reval_seq;
+        udpif->last_reval_seq = reval_seq;
+
+        n_flows = udpif_get_n_flows(udpif);
+        udpif->max_n_flows = MAX(n_flows, udpif->max_n_flows);
+        udpif->avg_n_flows = (udpif->avg_n_flows + n_flows) / 2;
+
+        atomic_read(&udpif->flow_limit, &flow_limit);
+        if (n_flows < flow_limit / 8) {
+            max_idle = 5000;
+        } else if (n_flows < flow_limit / 4) {
+            max_idle = 2000;
+        } else if (n_flows < flow_limit / 2) {
+            max_idle = 1000;
+        } else {
+            max_idle = 500;
+        }
+        atomic_store(&udpif->max_idle, max_idle);
+
+        start_time = time_msec();
+        dpif_flow_dump_start(&dump, udpif->dpif);
+        while (dpif_flow_dump_next(&dump, &key, &key_len, &mask, &mask_len,
+                                   NULL, NULL, &stats)
+               && !latch_is_set(&udpif->exit_latch)) {
+            struct udpif_flow_dump *udump = xmalloc(sizeof *udump);
+            struct revalidator *revalidator;
+
+            udump->key_hash = hash_bytes(key, key_len, udpif->secret);
+            memcpy(&udump->key_buf, key, key_len);
+            udump->key = (struct nlattr *) &udump->key_buf;
+            udump->key_len = key_len;
+
+            memcpy(&udump->mask_buf, mask, mask_len);
+            udump->mask = (struct nlattr *) &udump->mask_buf;
+            udump->mask_len = mask_len;
+
+            udump->stats = *stats;
+            udump->need_revalidate = need_revalidate;
+
+            revalidator = &udpif->revalidators[udump->key_hash
+                % udpif->n_revalidators];
+
+            ovs_mutex_lock(&revalidator->mutex);
+            while (revalidator->n_udumps >= REVALIDATE_MAX_BATCH * 3
+                   && !latch_is_set(&udpif->exit_latch)) {
+                ovs_mutex_cond_wait(&revalidator->wake_cond,
+                                    &revalidator->mutex);
+            }
+            list_push_back(&revalidator->udumps, &udump->list_node);
+            revalidator->n_udumps++;
+            xpthread_cond_signal(&revalidator->wake_cond);
+            ovs_mutex_unlock(&revalidator->mutex);
+        }
+        dpif_flow_dump_done(&dump);
+
+        /* Let all the revalidators finish and garbage collect. */
+        seq_change(udpif->dump_seq);
+        for (i = 0; i < udpif->n_revalidators; i++) {
+            struct revalidator *revalidator = &udpif->revalidators[i];
+            ovs_mutex_lock(&revalidator->mutex);
+            xpthread_cond_signal(&revalidator->wake_cond);
+            ovs_mutex_unlock(&revalidator->mutex);
+        }
+
+        for (i = 0; i < udpif->n_revalidators; i++) {
+            struct revalidator *revalidator = &udpif->revalidators[i];
+
+            ovs_mutex_lock(&revalidator->mutex);
+            while (revalidator->dump_seq != seq_read(udpif->dump_seq)
+                   && !latch_is_set(&udpif->exit_latch)) {
+                ovs_mutex_cond_wait(&revalidator->wake_cond,
+                                    &revalidator->mutex);
+            }
+            ovs_mutex_unlock(&revalidator->mutex);
+        }
+
+        duration = time_msec() - start_time;
+        udpif->dump_duration = duration;
+        if (duration > 2000) {
+            flow_limit /= duration / 1000;
+        } else if (duration > 1300) {
+            flow_limit = flow_limit * 3 / 4;
+        } else if (duration < 1000 && n_flows > 2000
+                   && flow_limit < n_flows * 1000 / duration) {
+            flow_limit += 1000;
+        }
+        flow_limit = MIN(ofproto_flow_limit, MAX(flow_limit, 1000));
+        atomic_store(&udpif->flow_limit, flow_limit);
+
+        if (duration > 2000) {
+            VLOG_INFO("Spent an unreasonably long %lldms dumping flows",
+                      duration);
+        }
+
+        poll_timer_wait_until(start_time + MIN(max_idle, 500));
+        seq_wait(udpif->reval_seq, udpif->last_reval_seq);
+        latch_wait(&udpif->exit_latch);
+        poll_block();
+    }
+
+    return NULL;
+}
+
 /* The miss handler thread is responsible for processing miss upcalls retrieved
  * by the dispatcher thread.  Once finished it passes the processed miss
  * upcalls to ofproto-dpif where they're installed in the datapath. */
@@ -394,7 +622,9 @@ udpif_upcall_handler(void *arg)
 {
     struct handler *handler = arg;
 
-    set_subprogram_name("upcall_%u", ovsthread_id_self());
+    handler->name = xasprintf("handler_%u", ovsthread_id_self());
+    set_subprogram_name("%s", handler->name);
+
     for (;;) {
         struct list misses = LIST_INITIALIZER(&misses);
         size_t i;
@@ -420,18 +650,57 @@ udpif_upcall_handler(void *arg)
         }
         ovs_mutex_unlock(&handler->mutex);
 
-        handle_upcalls(handler->udpif, &misses);
+        handle_upcalls(handler, &misses);
 
         coverage_clear();
     }
 }
-\f
-static void
-miss_destroy(struct flow_miss *miss)
+
+static void *
+udpif_revalidator(void *arg)
 {
-    xlate_out_uninit(&miss->xout);
-}
+    struct revalidator *revalidator = arg;
+
+    revalidator->name = xasprintf("revalidator_%u", ovsthread_id_self());
+    set_subprogram_name("%s", revalidator->name);
+    for (;;) {
+        struct list udumps = LIST_INITIALIZER(&udumps);
+        struct udpif *udpif = revalidator->udpif;
+        size_t i;
+
+        ovs_mutex_lock(&revalidator->mutex);
+        if (latch_is_set(&udpif->exit_latch)) {
+            ovs_mutex_unlock(&revalidator->mutex);
+            return NULL;
+        }
+
+        if (!revalidator->n_udumps) {
+            if (revalidator->dump_seq != seq_read(udpif->dump_seq)) {
+                revalidator->dump_seq = seq_read(udpif->dump_seq);
+                revalidator_sweep(revalidator);
+            } else {
+                ovs_mutex_cond_wait(&revalidator->wake_cond,
+                                    &revalidator->mutex);
+            }
+        }
+
+        for (i = 0; i < REVALIDATE_MAX_BATCH && revalidator->n_udumps; i++) {
+            list_push_back(&udumps, list_pop_front(&revalidator->udumps));
+            revalidator->n_udumps--;
+        }
+
+        /* Wake up the flow dumper. */
+        xpthread_cond_signal(&revalidator->wake_cond);
+        ovs_mutex_unlock(&revalidator->mutex);
+
+        if (!list_is_empty(&udumps)) {
+            revalidate_udumps(revalidator, &udumps);
+        }
+    }
 
+    return NULL;
+}
+\f
 static enum upcall_type
 classify_upcall(const struct upcall *upcall)
 {
@@ -506,7 +775,10 @@ recv_upcalls(struct udpif *udpif)
         error = dpif_recv(udpif->dpif, &upcall->dpif_upcall,
                           &upcall->upcall_buf);
         if (error) {
-            upcall_destroy(upcall);
+            /* upcall_destroy() can only be called on successfully received
+             * upcalls. */
+            ofpbuf_uninit(&upcall->upcall_buf);
+            free(upcall);
             break;
         }
 
@@ -571,6 +843,27 @@ recv_upcalls(struct udpif *udpif)
     }
 }
 
+/* Calculates slow path actions for 'xout'.  'buf' must statically be
+ * initialized with at least 128 bytes of space. */
+static void
+compose_slow_path(struct udpif *udpif, struct xlate_out *xout,
+                  odp_port_t odp_in_port, struct ofpbuf *buf)
+{
+    union user_action_cookie cookie;
+    odp_port_t port;
+    uint32_t pid;
+
+    cookie.type = USER_ACTION_COOKIE_SLOW_PATH;
+    cookie.slow_path.unused = 0;
+    cookie.slow_path.reason = xout->slow;
+
+    port = xout->slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP | SLOW_STP)
+        ? ODPP_NONE
+        : odp_in_port;
+    pid = dpif_port_get_pid(udpif->dpif, port);
+    odp_put_userspace_action(pid, &cookie, sizeof cookie.slow_path, buf);
+}
+
 static struct flow_miss *
 flow_miss_find(struct hmap *todo, const struct ofproto_dpif *ofproto,
                const struct flow *flow, uint32_t hash)
@@ -587,20 +880,26 @@ flow_miss_find(struct hmap *todo, const struct ofproto_dpif *ofproto,
 }
 
 static void
-handle_upcalls(struct udpif *udpif, struct list *upcalls)
+handle_upcalls(struct handler *handler, struct list *upcalls)
 {
-    struct dpif_op *opsp[FLOW_MISS_MAX_BATCH];
-    struct dpif_op ops[FLOW_MISS_MAX_BATCH];
+    struct hmap misses = HMAP_INITIALIZER(&misses);
+    struct udpif *udpif = handler->udpif;
+
+    struct flow_miss miss_buf[FLOW_MISS_MAX_BATCH];
+    struct dpif_op *opsp[FLOW_MISS_MAX_BATCH * 2];
+    struct dpif_op ops[FLOW_MISS_MAX_BATCH * 2];
+    struct flow_miss *miss, *next_miss;
     struct upcall *upcall, *next;
-    struct flow_miss_batch *fmb;
     size_t n_misses, n_ops, i;
-    struct flow_miss *miss;
-    unsigned int reval_seq;
+    unsigned int flow_limit;
+    bool fail_open, may_put;
     enum upcall_type type;
-    bool fail_open;
 
-    /* Extract the flow from each upcall.  Construct in fmb->misses a hash
-     * table that maps each unique flow to a 'struct flow_miss'.
+    atomic_read(&udpif->flow_limit, &flow_limit);
+    may_put = udpif_get_n_flows(udpif) < flow_limit;
+
+    /* Extract the flow from each upcall.  Construct in 'misses' a hash table
+     * that maps each unique flow to a 'struct flow_miss'.
      *
      * Most commonly there is a single packet per flow_miss, but there are
      * several reasons why there might be more than one, e.g.:
@@ -618,15 +917,11 @@ handle_upcalls(struct udpif *udpif, struct list *upcalls)
      *     other end of the connection, which gives OVS a chance to set up a
      *     datapath flow.)
      */
-    fmb = xmalloc(sizeof *fmb);
-    atomic_read(&udpif->reval_seq, &fmb->reval_seq);
-    hmap_init(&fmb->misses);
-    list_init(&fmb->upcalls);
     n_misses = 0;
     LIST_FOR_EACH_SAFE (upcall, next, list_node, upcalls) {
         struct dpif_upcall *dupcall = &upcall->dpif_upcall;
-        struct ofpbuf *packet = dupcall->packet;
-        struct flow_miss *miss = &fmb->miss_buf[n_misses];
+        struct flow_miss *miss = &miss_buf[n_misses];
+        struct ofpbuf *packet = &dupcall->packet;
         struct flow_miss *existing_miss;
         struct ofproto_dpif *ofproto;
         struct dpif_sflow *sflow;
@@ -637,11 +932,9 @@ handle_upcalls(struct udpif *udpif, struct list *upcalls)
 
         error = xlate_receive(udpif->backer, packet, dupcall->key,
                               dupcall->key_len, &flow, &miss->key_fitness,
-                              &ofproto, &odp_in_port);
+                              &ofproto, &ipfix, &sflow, NULL, &odp_in_port);
         if (error) {
             if (error == ENODEV) {
-                struct drop_key *drop_key;
-
                 /* Received packet on datapath port for which we couldn't
                  * associate an ofproto.  This can happen if a port is removed
                  * while traffic is being received.  Print a rate-limited
@@ -650,19 +943,9 @@ handle_upcalls(struct udpif *udpif, struct list *upcalls)
                  * in the kernel. */
                 VLOG_INFO_RL(&rl, "received packet on unassociated datapath "
                              "port %"PRIu32, odp_in_port);
-
-                drop_key = xmalloc(sizeof *drop_key);
-                drop_key->key = xmemdup(dupcall->key, dupcall->key_len);
-                drop_key->key_len = dupcall->key_len;
-
-                if (guarded_list_push_back(&udpif->drop_keys,
-                                           &drop_key->list_node,
-                                           MAX_QUEUE_LENGTH)) {
-                    seq_change(udpif->wait_seq);
-                } else {
-                    COVERAGE_INC(drop_queue_overflow);
-                    drop_key_destroy(drop_key);
-                }
+                dpif_flow_put(udpif->dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY,
+                              dupcall->key, dupcall->key_len, NULL, 0, NULL, 0,
+                              NULL);
             }
             list_remove(&upcall->list_node);
             upcall_destroy(upcall);
@@ -677,10 +960,10 @@ handle_upcalls(struct udpif *udpif, struct list *upcalls)
                          &flow.tunnel, &flow.in_port, &miss->flow);
 
             hash = flow_hash(&miss->flow, 0);
-            existing_miss = flow_miss_find(&fmb->misses, ofproto, &miss->flow,
+            existing_miss = flow_miss_find(&misses, ofproto, &miss->flow,
                                            hash);
             if (!existing_miss) {
-                hmap_insert(&fmb->misses, &miss->hmap_node, hash);
+                hmap_insert(&misses, &miss->hmap_node, hash);
                 miss->ofproto = ofproto;
                 miss->key = dupcall->key;
                 miss->key_len = dupcall->key_len;
@@ -689,6 +972,7 @@ handle_upcalls(struct udpif *udpif, struct list *upcalls)
                 miss->stats.n_bytes = 0;
                 miss->stats.used = time_msec();
                 miss->stats.tcp_flags = 0;
+                miss->odp_in_port = odp_in_port;
 
                 n_misses++;
             } else {
@@ -704,27 +988,22 @@ handle_upcalls(struct udpif *udpif, struct list *upcalls)
 
         switch (type) {
         case SFLOW_UPCALL:
-            sflow = xlate_get_sflow(ofproto);
             if (sflow) {
                 union user_action_cookie cookie;
 
                 memset(&cookie, 0, sizeof cookie);
                 memcpy(&cookie, nl_attr_get(dupcall->userdata),
                        sizeof cookie.sflow);
-                dpif_sflow_received(sflow, dupcall->packet, &flow, odp_in_port,
+                dpif_sflow_received(sflow, packet, &flow, odp_in_port,
                                     &cookie);
-                dpif_sflow_unref(sflow);
             }
             break;
         case IPFIX_UPCALL:
-            ipfix = xlate_get_ipfix(ofproto);
             if (ipfix) {
-                dpif_ipfix_bridge_sample(ipfix, dupcall->packet, &flow);
-                dpif_ipfix_unref(ipfix);
+                dpif_ipfix_bridge_sample(ipfix, packet, &flow);
             }
             break;
         case FLOW_SAMPLE_UPCALL:
-            ipfix = xlate_get_ipfix(ofproto);
             if (ipfix) {
                 union user_action_cookie cookie;
 
@@ -734,20 +1013,22 @@ handle_upcalls(struct udpif *udpif, struct list *upcalls)
 
                 /* The flow reflects exactly the contents of the packet.
                  * Sample the packet using it. */
-                dpif_ipfix_flow_sample(ipfix, dupcall->packet, &flow,
+                dpif_ipfix_flow_sample(ipfix, packet, &flow,
                                        cookie.flow_sample.collector_set_id,
                                        cookie.flow_sample.probability,
                                        cookie.flow_sample.obs_domain_id,
                                        cookie.flow_sample.obs_point_id);
-                dpif_ipfix_unref(ipfix);
             }
             break;
         case BAD_UPCALL:
             break;
         case MISS_UPCALL:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
 
+        dpif_ipfix_unref(ipfix);
+        dpif_sflow_unref(sflow);
+
         list_remove(&upcall->list_node);
         upcall_destroy(upcall);
     }
@@ -760,13 +1041,21 @@ handle_upcalls(struct udpif *udpif, struct list *upcalls)
      * We can't do this in the previous loop because we need the TCP flags for
      * all the packets in each miss. */
     fail_open = false;
-    HMAP_FOR_EACH (miss, hmap_node, &fmb->misses) {
+    HMAP_FOR_EACH (miss, hmap_node, &misses) {
         struct xlate_in xin;
 
         xlate_in_init(&xin, miss->ofproto, &miss->flow, NULL,
                       miss->stats.tcp_flags, NULL);
         xin.may_learn = true;
-        xin.resubmit_stats = &miss->stats;
+
+        if (miss->upcall_type == DPIF_UC_MISS) {
+            xin.resubmit_stats = &miss->stats;
+        } else {
+            /* For non-miss upcalls, there's a flow in the datapath which this
+             * packet was accounted to.  Presumably the revalidators will deal
+             * with pushing its stats eventually. */
+        }
+
         xlate_actions(&xin, &miss->xout);
         fail_open = fail_open || miss->xout.fail_open;
     }
@@ -786,7 +1075,10 @@ handle_upcalls(struct udpif *udpif, struct list *upcalls)
     n_ops = 0;
     LIST_FOR_EACH (upcall, list_node, upcalls) {
         struct flow_miss *miss = upcall->flow_miss;
-        struct ofpbuf *packet = upcall->dpif_upcall.packet;
+        struct ofpbuf *packet = &upcall->dpif_upcall.packet;
+        struct ofpbuf mask;
+        struct dpif_op *op;
+        bool megaflow;
 
         if (miss->xout.slow) {
             struct xlate_in xin;
@@ -795,9 +1087,38 @@ handle_upcalls(struct udpif *udpif, struct list *upcalls)
             xlate_actions_for_side_effects(&xin);
         }
 
-        if (miss->xout.odp_actions.size) {
-            struct dpif_op *op;
+        atomic_read(&enable_megaflows, &megaflow);
+        ofpbuf_use_stack(&mask, &miss->mask_buf, sizeof miss->mask_buf);
+        if (megaflow) {
+            odp_flow_key_from_mask(&mask, &miss->xout.wc.masks, &miss->flow,
+                                   UINT32_MAX);
+        }
+
+        if (may_put) {
+            op = &ops[n_ops++];
+            op->type = DPIF_OP_FLOW_PUT;
+            op->u.flow_put.flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
+            op->u.flow_put.key = miss->key;
+            op->u.flow_put.key_len = miss->key_len;
+            op->u.flow_put.mask = mask.data;
+            op->u.flow_put.mask_len = mask.size;
+            op->u.flow_put.stats = NULL;
+
+            if (!miss->xout.slow) {
+                op->u.flow_put.actions = miss->xout.odp_actions.data;
+                op->u.flow_put.actions_len = miss->xout.odp_actions.size;
+            } else {
+                struct ofpbuf buf;
+
+                ofpbuf_use_stack(&buf, miss->slow_path_buf,
+                                 sizeof miss->slow_path_buf);
+                compose_slow_path(udpif, &miss->xout, miss->odp_in_port, &buf);
+                op->u.flow_put.actions = buf.data;
+                op->u.flow_put.actions_len = buf.size;
+            }
+        }
 
+        if (miss->xout.odp_actions.size) {
             if (miss->flow.in_port.ofp_port
                 != vsp_realdev_to_vlandev(miss->ofproto,
                                           miss->flow.in_port.ofp_port,
@@ -813,32 +1134,28 @@ handle_upcalls(struct udpif *udpif, struct list *upcalls)
 
             op = &ops[n_ops++];
             op->type = DPIF_OP_EXECUTE;
-            op->u.execute.key = miss->key;
-            op->u.execute.key_len = miss->key_len;
             op->u.execute.packet = packet;
+            odp_key_to_pkt_metadata(miss->key, miss->key_len,
+                                    &op->u.execute.md);
             op->u.execute.actions = miss->xout.odp_actions.data;
             op->u.execute.actions_len = miss->xout.odp_actions.size;
             op->u.execute.needs_help = (miss->xout.slow & SLOW_ACTION) != 0;
         }
     }
 
-    /* Execute batch. */
-    for (i = 0; i < n_ops; i++) {
-        opsp[i] = &ops[i];
-    }
-    dpif_operate(udpif->dpif, opsp, n_ops);
-
     /* Special case for fail-open mode.
      *
      * If we are in fail-open mode, but we are connected to a controller too,
      * then we should send the packet up to the controller in the hope that it
      * will try to set up a flow and thereby allow us to exit fail-open.
      *
-     * See the top-level comment in fail-open.c for more information. */
+     * See the top-level comment in fail-open.c for more information.
+     *
+     * Copy packets before they are modified by execution. */
     if (fail_open) {
         LIST_FOR_EACH (upcall, list_node, upcalls) {
             struct flow_miss *miss = upcall->flow_miss;
-            struct ofpbuf *packet = upcall->dpif_upcall.packet;
+            struct ofpbuf *packet = &upcall->dpif_upcall.packet;
             struct ofproto_packet_in *pin;
 
             pin = xmalloc(sizeof *pin);
@@ -854,17 +1171,372 @@ handle_upcalls(struct udpif *udpif, struct list *upcalls)
         }
     }
 
-    list_move(&fmb->upcalls, upcalls);
+    /* Execute batch. */
+    for (i = 0; i < n_ops; i++) {
+        opsp[i] = &ops[i];
+    }
+    dpif_operate(udpif->dpif, opsp, n_ops);
+
+    HMAP_FOR_EACH_SAFE (miss, next_miss, hmap_node, &misses) {
+        hmap_remove(&misses, &miss->hmap_node);
+        xlate_out_uninit(&miss->xout);
+    }
+    hmap_destroy(&misses);
+
+    LIST_FOR_EACH_SAFE (upcall, next, list_node, upcalls) {
+        list_remove(&upcall->list_node);
+        upcall_destroy(upcall);
+    }
+}
+
+static struct udpif_key *
+ukey_lookup(struct revalidator *revalidator, struct udpif_flow_dump *udump)
+{
+    struct udpif_key *ukey;
+
+    HMAP_FOR_EACH_WITH_HASH (ukey, hmap_node, udump->key_hash,
+                             &revalidator->ukeys) {
+        if (ukey->key_len == udump->key_len
+            && !memcmp(ukey->key, udump->key, udump->key_len)) {
+            return ukey;
+        }
+    }
+    return NULL;
+}
 
-    atomic_read(&udpif->reval_seq, &reval_seq);
-    if (reval_seq != fmb->reval_seq) {
-        COVERAGE_INC(fmb_queue_revalidated);
-        flow_miss_batch_destroy(fmb);
-    } else if (!guarded_list_push_back(&udpif->fmbs, &fmb->list_node,
-                                       MAX_QUEUE_LENGTH)) {
-        COVERAGE_INC(fmb_queue_overflow);
-        flow_miss_batch_destroy(fmb);
+static void
+ukey_delete(struct revalidator *revalidator, struct udpif_key *ukey)
+{
+    hmap_remove(&revalidator->ukeys, &ukey->hmap_node);
+    free(ukey);
+}
+
+static bool
+revalidate_ukey(struct udpif *udpif, struct udpif_flow_dump *udump,
+                struct udpif_key *ukey)
+{
+    struct ofpbuf xout_actions, *actions;
+    uint64_t slow_path_buf[128 / 8];
+    struct xlate_out xout, *xoutp;
+    struct flow flow, udump_mask;
+    struct ofproto_dpif *ofproto;
+    struct dpif_flow_stats push;
+    uint32_t *udump32, *xout32;
+    odp_port_t odp_in_port;
+    struct xlate_in xin;
+    int error;
+    size_t i;
+    bool ok;
+
+    ok = false;
+    xoutp = NULL;
+    actions = NULL;
+
+    /* If we don't need to revalidate, we can simply push the stats contained
+     * in the udump, otherwise we'll have to get the actions so we can check
+     * them. */
+    if (udump->need_revalidate) {
+        if (dpif_flow_get(udpif->dpif, ukey->key, ukey->key_len, &actions,
+                          &udump->stats)) {
+            goto exit;
+        }
+    }
+
+    push.used = udump->stats.used;
+    push.tcp_flags = udump->stats.tcp_flags;
+    push.n_packets = udump->stats.n_packets > ukey->stats.n_packets
+        ? udump->stats.n_packets - ukey->stats.n_packets
+        : 0;
+    push.n_bytes = udump->stats.n_bytes > ukey->stats.n_bytes
+        ? udump->stats.n_bytes - ukey->stats.n_bytes
+        : 0;
+    ukey->stats = udump->stats;
+
+    if (!push.n_packets && !udump->need_revalidate) {
+        ok = true;
+        goto exit;
+    }
+
+    error = xlate_receive(udpif->backer, NULL, ukey->key, ukey->key_len, &flow,
+                          NULL, &ofproto, NULL, NULL, NULL, &odp_in_port);
+    if (error) {
+        goto exit;
+    }
+
+    xlate_in_init(&xin, ofproto, &flow, NULL, push.tcp_flags, NULL);
+    xin.resubmit_stats = push.n_packets ? &push : NULL;
+    xin.may_learn = push.n_packets > 0;
+    xin.skip_wildcards = !udump->need_revalidate;
+    xlate_actions(&xin, &xout);
+    xoutp = &xout;
+
+    if (!udump->need_revalidate) {
+        ok = true;
+        goto exit;
+    }
+
+    if (!xout.slow) {
+        ofpbuf_use_const(&xout_actions, xout.odp_actions.data,
+                         xout.odp_actions.size);
     } else {
-        seq_change(udpif->wait_seq);
+        ofpbuf_use_stack(&xout_actions, slow_path_buf, sizeof slow_path_buf);
+        compose_slow_path(udpif, &xout, odp_in_port, &xout_actions);
+    }
+
+    if (!ofpbuf_equal(&xout_actions, actions)) {
+        goto exit;
+    }
+
+    if (odp_flow_key_to_mask(udump->mask, udump->mask_len, &udump_mask, &flow)
+        == ODP_FIT_ERROR) {
+        goto exit;
+    }
+
+    /* Since the kernel is free to ignore wildcarded bits in the mask, we can't
+     * directly check that the masks are the same.  Instead we check that the
+     * mask in the kernel is more specific i.e. less wildcarded, than what
+     * we've calculated here.  This guarantees we don't catch any packets we
+     * shouldn't with the megaflow. */
+    udump32 = (uint32_t *) &udump_mask;
+    xout32 = (uint32_t *) &xout.wc.masks;
+    for (i = 0; i < FLOW_U32S; i++) {
+        if ((udump32[i] | xout32[i]) != udump32[i]) {
+            goto exit;
+        }
+    }
+    ok = true;
+
+exit:
+    ofpbuf_delete(actions);
+    xlate_out_uninit(xoutp);
+    return ok;
+}
+
+static void
+revalidate_udumps(struct revalidator *revalidator, struct list *udumps)
+{
+    struct udpif *udpif = revalidator->udpif;
+
+    struct {
+        struct dpif_flow_stats ukey_stats;    /* Stats stored in the ukey. */
+        struct dpif_flow_stats stats;         /* Stats for 'op'. */
+        struct dpif_op op;                    /* Flow del operation. */
+    } ops[REVALIDATE_MAX_BATCH];
+
+    struct dpif_op *opsp[REVALIDATE_MAX_BATCH];
+    struct udpif_flow_dump *udump, *next_udump;
+    size_t n_ops, i, n_flows;
+    unsigned int flow_limit;
+    long long int max_idle;
+    bool must_del;
+
+    atomic_read(&udpif->max_idle, &max_idle);
+    atomic_read(&udpif->flow_limit, &flow_limit);
+
+    n_flows = udpif_get_n_flows(udpif);
+
+    must_del = false;
+    if (n_flows > flow_limit) {
+        must_del = n_flows > 2 * flow_limit;
+        max_idle = 100;
+    }
+
+    n_ops = 0;
+    LIST_FOR_EACH_SAFE (udump, next_udump, list_node, udumps) {
+        long long int used, now;
+        struct udpif_key *ukey;
+
+        now = time_msec();
+        ukey = ukey_lookup(revalidator, udump);
+
+        used = udump->stats.used;
+        if (!used && ukey) {
+            used = ukey->created;
+        }
+
+        if (must_del || (used && used < now - max_idle)) {
+            struct dpif_flow_stats *ukey_stats = &ops[n_ops].ukey_stats;
+            struct dpif_op *op = &ops[n_ops].op;
+
+            op->type = DPIF_OP_FLOW_DEL;
+            op->u.flow_del.key = udump->key;
+            op->u.flow_del.key_len = udump->key_len;
+            op->u.flow_del.stats = &ops[n_ops].stats;
+            n_ops++;
+
+            if (ukey) {
+                *ukey_stats = ukey->stats;
+                ukey_delete(revalidator, ukey);
+            } else {
+                memset(ukey_stats, 0, sizeof *ukey_stats);
+            }
+
+            continue;
+        }
+
+        if (!ukey) {
+            ukey = xmalloc(sizeof *ukey);
+
+            ukey->key = (struct nlattr *) &ukey->key_buf;
+            memcpy(ukey->key, udump->key, udump->key_len);
+            ukey->key_len = udump->key_len;
+
+            ukey->created = used ? used : now;
+            memset(&ukey->stats, 0, sizeof ukey->stats);
+
+            ukey->mark = false;
+
+            hmap_insert(&revalidator->ukeys, &ukey->hmap_node,
+                        udump->key_hash);
+        }
+        ukey->mark = true;
+
+        if (!revalidate_ukey(udpif, udump, ukey)) {
+            dpif_flow_del(udpif->dpif, udump->key, udump->key_len, NULL);
+            ukey_delete(revalidator, ukey);
+        }
+
+        list_remove(&udump->list_node);
+        free(udump);
+    }
+
+    for (i = 0; i < n_ops; i++) {
+        opsp[i] = &ops[i].op;
+    }
+    dpif_operate(udpif->dpif, opsp, n_ops);
+
+    for (i = 0; i < n_ops; i++) {
+        struct dpif_flow_stats push, *stats, *ukey_stats;
+
+        ukey_stats  = &ops[i].ukey_stats;
+        stats = ops[i].op.u.flow_del.stats;
+        push.used = MAX(stats->used, ukey_stats->used);
+        push.tcp_flags = stats->tcp_flags | ukey_stats->tcp_flags;
+        push.n_packets = stats->n_packets - ukey_stats->n_packets;
+        push.n_bytes = stats->n_bytes - ukey_stats->n_bytes;
+
+        if (push.n_packets || netflow_exists()) {
+            struct ofproto_dpif *ofproto;
+            struct netflow *netflow;
+            struct flow flow;
+
+            if (!xlate_receive(udpif->backer, NULL, ops[i].op.u.flow_del.key,
+                               ops[i].op.u.flow_del.key_len, &flow, NULL,
+                               &ofproto, NULL, NULL, &netflow, NULL)) {
+                struct xlate_in xin;
+
+                xlate_in_init(&xin, ofproto, &flow, NULL, push.tcp_flags,
+                              NULL);
+                xin.resubmit_stats = push.n_packets ? &push : NULL;
+                xin.may_learn = push.n_packets > 0;
+                xin.skip_wildcards = true;
+                xlate_actions_for_side_effects(&xin);
+
+                if (netflow) {
+                    netflow_expire(netflow, &flow);
+                    netflow_flow_clear(netflow, &flow);
+                    netflow_unref(netflow);
+                }
+            }
+        }
+    }
+
+    LIST_FOR_EACH_SAFE (udump, next_udump, list_node, udumps) {
+        list_remove(&udump->list_node);
+        free(udump);
+    }
+}
+
+static void
+revalidator_sweep(struct revalidator *revalidator)
+{
+    struct udpif_key *ukey, *next;
+
+    HMAP_FOR_EACH_SAFE (ukey, next, hmap_node, &revalidator->ukeys) {
+        if (ukey->mark) {
+            ukey->mark = false;
+        } else {
+            ukey_delete(revalidator, ukey);
+        }
+    }
+}
+\f
+static void
+upcall_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                    const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    struct udpif *udpif;
+
+    LIST_FOR_EACH (udpif, list_node, &all_udpifs) {
+        unsigned int flow_limit;
+        long long int max_idle;
+        size_t i;
+
+        atomic_read(&udpif->flow_limit, &flow_limit);
+        atomic_read(&udpif->max_idle, &max_idle);
+
+        ds_put_format(&ds, "%s:\n", dpif_name(udpif->dpif));
+        ds_put_format(&ds, "\tflows         : (current %"PRIu64")"
+            " (avg %u) (max %u) (limit %u)\n", udpif_get_n_flows(udpif),
+            udpif->avg_n_flows, udpif->max_n_flows, flow_limit);
+        ds_put_format(&ds, "\tmax idle      : %lldms\n", max_idle);
+        ds_put_format(&ds, "\tdump duration : %lldms\n", udpif->dump_duration);
+
+        ds_put_char(&ds, '\n');
+        for (i = 0; i < udpif->n_handlers; i++) {
+            struct handler *handler = &udpif->handlers[i];
+
+            ovs_mutex_lock(&handler->mutex);
+            ds_put_format(&ds, "\t%s: (upcall queue %"PRIuSIZE")\n",
+                          handler->name, handler->n_upcalls);
+            ovs_mutex_unlock(&handler->mutex);
+        }
+
+        ds_put_char(&ds, '\n');
+        for (i = 0; i < n_revalidators; i++) {
+            struct revalidator *revalidator = &udpif->revalidators[i];
+
+            /* XXX: The result of hmap_count(&revalidator->ukeys) may not be
+             * accurate because it's not protected by the revalidator mutex. */
+            ovs_mutex_lock(&revalidator->mutex);
+            ds_put_format(&ds, "\t%s: (dump queue %"PRIuSIZE") (keys %"PRIuSIZE
+                          ")\n", revalidator->name, revalidator->n_udumps,
+                          hmap_count(&revalidator->ukeys));
+            ovs_mutex_unlock(&revalidator->mutex);
+        }
     }
+
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+}
+
+/* Disable using the megaflows.
+ *
+ * This command is only needed for advanced debugging, so it's not
+ * documented in the man page. */
+static void
+upcall_unixctl_disable_megaflows(struct unixctl_conn *conn,
+                                 int argc OVS_UNUSED,
+                                 const char *argv[] OVS_UNUSED,
+                                 void *aux OVS_UNUSED)
+{
+    atomic_store(&enable_megaflows, false);
+    udpif_flush();
+    unixctl_command_reply(conn, "megaflows disabled");
+}
+
+/* Re-enable using megaflows.
+ *
+ * This command is only needed for advanced debugging, so it's not
+ * documented in the man page. */
+static void
+upcall_unixctl_enable_megaflows(struct unixctl_conn *conn,
+                                int argc OVS_UNUSED,
+                                const char *argv[] OVS_UNUSED,
+                                void *aux OVS_UNUSED)
+{
+    atomic_store(&enable_megaflows, true);
+    udpif_flush();
+    unixctl_command_reply(conn, "megaflows enabled");
 }
index da75719..d73ae4c 100644 (file)
 #ifndef OFPROTO_DPIF_UPCALL_H
 #define OFPROTO_DPIF_UPCALL_H
 
-#define FLOW_MISS_MAX_BATCH 50
-
-#include "dpif.h"
-#include "flow.h"
-#include "hmap.h"
-#include "list.h"
-#include "odp-util.h"
-#include "ofpbuf.h"
-#include "ofproto-dpif-xlate.h"
+#include <stddef.h>
 
 struct dpif;
 struct dpif_backer;
+struct seq;
+struct simap;
 
-/* udif is responsible for retrieving upcalls from the kernel, processing miss
- * upcalls, and handing more complex ones up to the main ofproto-dpif
- * module. */
+/* Udif is responsible for retrieving upcalls from the kernel and processing
+ * them.  Additionally, it's responsible for maintaining the datapath flow
+ * table. */
 
 struct udpif *udpif_create(struct dpif_backer *, struct dpif *);
-void udpif_recv_set(struct udpif *, size_t n_workers, bool enable);
+void udpif_set_threads(struct udpif *, size_t n_handlers,
+                       size_t n_revalidators);
 void udpif_destroy(struct udpif *);
-
-void udpif_wait(struct udpif *);
-
 void udpif_revalidate(struct udpif *);
-\f
-/* udpif figures out how to forward packets, and does forward them, but it
- * can't set up datapath flows on its own.  This interface passes packet
- * forwarding data from udpif to the higher level ofproto_dpif to allow the
- * latter to set up datapath flows. */
-
-/* Flow miss batching.
- *
- * Some dpifs implement operations faster when you hand them off in a batch.
- * To allow batching, "struct flow_miss" queues the dpif-related work needed
- * for a given flow.  Each "struct flow_miss" corresponds to sending one or
- * more packets, plus possibly installing the flow in the dpif. */
-struct flow_miss {
-    struct hmap_node hmap_node;
-    struct ofproto_dpif *ofproto;
-
-    struct flow flow;
-    enum odp_key_fitness key_fitness;
-    const struct nlattr *key;
-    size_t key_len;
-    enum dpif_upcall_type upcall_type;
-    struct dpif_flow_stats stats;
-
-    struct xlate_out xout;
-};
-
-struct flow_miss_batch {
-    struct list list_node;
-
-    struct flow_miss miss_buf[FLOW_MISS_MAX_BATCH];
-    struct hmap misses;
-
-    unsigned int reval_seq;
-
-    /* Flow misses refer to the memory held by "struct upcall"s,
-     * so we need to keep track of the upcalls to be able to
-     * free them when done. */
-    struct list upcalls;        /* Contains "struct upcall"s. */
-};
-
-struct flow_miss_batch *flow_miss_batch_next(struct udpif *);
-void flow_miss_batch_destroy(struct flow_miss_batch *);
-\f
-/* Drop keys are odp flow keys which have drop flows installed in the kernel.
- * These are datapath flows which have no associated ofproto, if they did we
- * would use facets.
- *
- * udpif can't install drop flows by itself.  This interfaces allows udpif to
- * pass the drop flows up to ofproto_dpif to get it to install them. */
-struct drop_key {
-    struct hmap_node hmap_node;
-    struct list list_node;
-    struct nlattr *key;
-    size_t key_len;
-};
-
-struct drop_key *drop_key_next(struct udpif *);
-void drop_key_destroy(struct drop_key *);
-void udpif_drop_key_clear(struct udpif *);
+void udpif_get_memory_usage(struct udpif *, struct simap *usage);
+struct seq *udpif_dump_seq(struct udpif *);
+void udpif_flush(void);
 
 #endif /* ofproto-dpif-upcall.h */
index b0f61ca..13a5d07 100644 (file)
@@ -42,6 +42,7 @@
 #include "ofp-actions.h"
 #include "ofproto/ofproto-dpif-ipfix.h"
 #include "ofproto/ofproto-dpif-mirror.h"
+#include "ofproto/ofproto-dpif-monitor.h"
 #include "ofproto/ofproto-dpif-sflow.h"
 #include "ofproto/ofproto-dpif.h"
 #include "ofproto/ofproto-provider.h"
@@ -76,6 +77,7 @@ struct xbridge {
     struct mbridge *mbridge;      /* Mirroring. */
     struct dpif_sflow *sflow;     /* SFlow handle, or null. */
     struct dpif_ipfix *ipfix;     /* Ipfix handle, or null. */
+    struct netflow *netflow;      /* Netflow handle, or null. */
     struct stp *stp;              /* STP or null if disabled. */
 
     /* Special rules installed by ofproto-dpif. */
@@ -83,9 +85,13 @@ struct xbridge {
     struct rule_dpif *no_packet_in_rule;
 
     enum ofp_config_flags frag;   /* Fragmentation handling. */
-    bool has_netflow;             /* Bridge runs netflow? */
     bool has_in_band;             /* Bridge has in band control? */
     bool forward_bpdu;            /* Bridge forwards STP BPDUs? */
+
+    /* True if the datapath supports variable-length
+     * OVS_USERSPACE_ATTR_USERDATA in OVS_ACTION_ATTR_USERSPACE actions.
+     * False if the datapath supports only 8-byte (or shorter) userdata. */
+    bool variable_length_userdata;
 };
 
 struct xbundle {
@@ -219,10 +225,10 @@ static void do_xlate_actions(const struct ofpact *, size_t ofpacts_len,
                              struct xlate_ctx *);
 static void xlate_actions__(struct xlate_in *, struct xlate_out *)
     OVS_REQ_RDLOCK(xlate_rwlock);
-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 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 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 *,
@@ -246,8 +252,10 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
                   const struct mac_learning *ml, struct stp *stp,
                   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)
+                  const struct dpif_ipfix *ipfix,
+                  const struct netflow *netflow, enum ofp_config_flags frag,
+                  bool forward_bpdu, bool has_in_band,
+                  bool variable_length_userdata)
 {
     struct xbridge *xbridge = xbridge_lookup(ofproto);
 
@@ -285,16 +293,21 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
         xbridge->stp = stp_ref(stp);
     }
 
+    if (xbridge->netflow != netflow) {
+        netflow_unref(xbridge->netflow);
+        xbridge->netflow = netflow_ref(netflow);
+    }
+
     free(xbridge->name);
     xbridge->name = xstrdup(name);
 
     xbridge->dpif = dpif;
     xbridge->forward_bpdu = forward_bpdu;
     xbridge->has_in_band = has_in_band;
-    xbridge->has_netflow = has_netflow;
     xbridge->frag = frag;
     xbridge->miss_rule = miss_rule;
     xbridge->no_packet_in_rule = no_packet_in_rule;
+    xbridge->variable_length_userdata = variable_length_userdata;
 }
 
 void
@@ -509,8 +522,10 @@ xlate_ofport_remove(struct ofport_dpif *ofport)
  * respectively), populates 'flow' with the result of odp_flow_key_to_flow().
  * Optionally, if nonnull, populates 'fitnessp' with the fitness of 'flow' as
  * returned by odp_flow_key_to_flow().  Also, optionally populates 'ofproto'
- * with the ofproto_dpif, and 'odp_in_port' with the datapath in_port, that
- * 'packet' ingressed.
+ * with the ofproto_dpif, 'odp_in_port' with the datapath in_port, that
+ * 'packet' ingressed, and 'ipfix', 'sflow', and 'netflow' with the appropriate
+ * handles for those protocols if they're enabled.  Caller is responsible for
+ * unrefing them.
  *
  * If 'ofproto' is nonnull, requires 'flow''s in_port to exist.  Otherwise sets
  * 'flow''s in_port to OFPP_NONE.
@@ -532,7 +547,9 @@ int
 xlate_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
               const struct nlattr *key, size_t key_len,
               struct flow *flow, enum odp_key_fitness *fitnessp,
-              struct ofproto_dpif **ofproto, odp_port_t *odp_in_port)
+              struct ofproto_dpif **ofproto, struct dpif_ipfix **ipfix,
+              struct dpif_sflow **sflow, struct netflow **netflow,
+              odp_port_t *odp_in_port)
 {
     enum odp_key_fitness fitness;
     const struct xport *xport;
@@ -550,8 +567,8 @@ xlate_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
     }
 
     xport = xport_lookup(tnl_port_should_receive(flow)
-            ? tnl_port_receive(flow)
-            : odp_port_to_ofport(backer, flow->in_port.odp_port));
+                         ? tnl_port_receive(flow)
+                         : odp_port_to_ofport(backer, flow->in_port.odp_port));
 
     flow->in_port.ofp_port = xport ? xport->ofp_port : OFPP_NONE;
     if (!xport) {
@@ -563,18 +580,7 @@ xlate_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
             /* Make the packet resemble the flow, so that it gets sent to
              * an OpenFlow controller properly, so that it looks correct
              * for sFlow, and so that flow_extract() will get the correct
-             * vlan_tci if it is called on 'packet'.
-             *
-             * The allocated space inside 'packet' probably also contains
-             * 'key', that is, both 'packet' and 'key' are probably part of
-             * a struct dpif_upcall (see the large comment on that
-             * structure definition), so pushing data on 'packet' is in
-             * general not a good idea since it could overwrite 'key' or
-             * free it as a side effect.  However, it's OK in this special
-             * case because we know that 'packet' is inside a Netlink
-             * attribute: pushing 4 bytes will just overwrite the 4-byte
-             * "struct nlattr", which is fine since we don't need that
-             * header anymore. */
+             * vlan_tci if it is called on 'packet'. */
             eth_push_vlan(packet, flow->vlan_tci);
         }
         /* We can't reproduce 'key' from 'flow'. */
@@ -586,6 +592,18 @@ xlate_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
         *ofproto = xport->xbridge->ofproto;
     }
 
+    if (ipfix) {
+        *ipfix = dpif_ipfix_ref(xport->xbridge->ipfix);
+    }
+
+    if (sflow) {
+        *sflow = dpif_sflow_ref(xport->xbridge->sflow);
+    }
+
+    if (netflow) {
+        *netflow = netflow_ref(xport->xbridge->netflow);
+    }
+
 exit:
     if (fitnessp) {
         *fitnessp = fitness;
@@ -773,10 +791,10 @@ bucket_is_alive(const struct xlate_ctx *ctx,
     }
 
     return !ofputil_bucket_has_liveness(bucket) ||
-           (bucket->watch_port != OFPP_ANY &&
-            odp_port_is_alive(ctx, bucket->watch_port)) ||
-           (bucket->watch_group != OFPG_ANY &&
-            group_is_alive(ctx, bucket->watch_group, depth + 1));
+        (bucket->watch_port != OFPP_ANY &&
+         odp_port_is_alive(ctx, bucket->watch_port)) ||
+        (bucket->watch_group != OFPG_ANY &&
+         group_is_alive(ctx, bucket->watch_group, depth + 1));
 }
 
 static const struct ofputil_bucket *
@@ -958,7 +976,7 @@ add_mirror_actions(struct xlate_ctx *ctx, const struct flow *orig_flow)
         bool has_mirror;
         int out_vlan;
 
-        has_mirror = mirror_get(xbridge->mbridge, mirror_mask_ffs(mirrors) - 1,
+        has_mirror = mirror_get(xbridge->mbridge, raw_ctz(mirrors),
                                 &vlans, &dup_mirrors, &out, &out_vlan);
         ovs_assert(has_mirror);
 
@@ -1016,7 +1034,7 @@ input_vid_to_vlan(const struct xbundle *in_xbundle, uint16_t vid)
         return vid ? vid : in_xbundle->vlan;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1069,7 +1087,7 @@ input_vid_is_valid(uint16_t vid, struct xbundle *in_xbundle, bool warn)
         return true;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
 }
@@ -1096,7 +1114,7 @@ output_vlan_to_vid(const struct xbundle *out_xbundle, uint16_t vlan)
         return vlan == out_xbundle->vlan ? 0 : vlan;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1127,6 +1145,11 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
             /* No slaves enabled, so drop packet. */
             return;
         }
+
+        if (ctx->xin->resubmit_stats) {
+            bond_account(out_xbundle->bond, &ctx->xin->flow, vid,
+                         ctx->xin->resubmit_stats->n_bytes);
+        }
     }
 
     old_tci = *flow_tci;
@@ -1186,7 +1209,7 @@ is_mac_learning_update_needed(const struct mac_learning *ml,
                               const struct flow *flow,
                               struct flow_wildcards *wc,
                               int vlan, struct xbundle *in_xbundle)
-    OVS_REQ_RDLOCK(ml->rwlock)
+OVS_REQ_RDLOCK(ml->rwlock)
 {
     struct mac_entry *mac;
 
@@ -1226,7 +1249,7 @@ static void
 update_learning_table__(const struct xbridge *xbridge,
                         const struct flow *flow, struct flow_wildcards *wc,
                         int vlan, struct xbundle *in_xbundle)
-    OVS_REQ_WRLOCK(xbridge->ml->rwlock)
+OVS_REQ_WRLOCK(xbridge->ml->rwlock)
 {
     struct mac_entry *mac;
 
@@ -1335,7 +1358,7 @@ is_admissible(struct xlate_ctx *ctx, struct xport *in_port,
                  || mac_entry_is_grat_arp_locked(mac))) {
                 ovs_rwlock_unlock(&xbridge->ml->rwlock);
                 xlate_report(ctx, "SLB bond thinks this packet looped back, "
-                            "dropping");
+                             "dropping");
                 return false;
             }
             ovs_rwlock_unlock(&xbridge->ml->rwlock);
@@ -1630,6 +1653,14 @@ process_special(struct xlate_ctx *ctx, const struct flow *flow,
     } else if (xport->bfd && bfd_should_process_flow(xport->bfd, flow, wc)) {
         if (packet) {
             bfd_process_packet(xport->bfd, flow, packet);
+            /* If POLL received, immediately sends FINAL back. */
+            if (bfd_should_send_packet(xport->bfd)) {
+                if (xport->peer) {
+                    ofproto_dpif_monitor_port_send_soon(xport->ofport);
+                } else {
+                    ofproto_dpif_monitor_port_send_soon_safe(xport->ofport);
+                }
+            }
         }
         return SLOW_BFD;
     } else if (xport->xbundle && xport->xbundle->lacp
@@ -1847,9 +1878,10 @@ xlate_table_action(struct xlate_ctx *ctx,
                    ofp_port_t in_port, uint8_t table_id, bool may_packet_in)
 {
     if (xlate_resubmit_resource_check(ctx)) {
-        struct rule_dpif *rule;
         ofp_port_t old_in_port = ctx->xin->flow.in_port.ofp_port;
+        bool skip_wildcards = ctx->xin->skip_wildcards;
         uint8_t old_table_id = ctx->table_id;
+        struct rule_dpif *rule;
 
         ctx->table_id = table_id;
 
@@ -1857,8 +1889,8 @@ xlate_table_action(struct xlate_ctx *ctx,
          * original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will
          * have surprising behavior). */
         ctx->xin->flow.in_port.ofp_port = in_port;
-        rule_dpif_lookup_in_table(ctx->xbridge->ofproto,
-                                  &ctx->xin->flow, &ctx->xout->wc,
+        rule_dpif_lookup_in_table(ctx->xbridge->ofproto, &ctx->xin->flow,
+                                  !skip_wildcards ? &ctx->xout->wc : NULL,
                                   table_id, &rule);
         ctx->xin->flow.in_port.ofp_port = old_in_port;
 
@@ -1972,7 +2004,7 @@ xlate_group_action__(struct xlate_ctx *ctx, struct group_dpif *group)
         xlate_ff_group(ctx, group);
         break;
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
     group_dpif_release(group);
 }
@@ -2042,7 +2074,7 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
 {
     struct ofproto_packet_in *pin;
     struct ofpbuf *packet;
-    struct flow key;
+    struct pkt_metadata md = PKT_METADATA_INITIALIZER(0);
 
     ctx->xout->slow |= SLOW_CONTROLLER;
     if (!ctx->xin->packet) {
@@ -2051,17 +2083,13 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
 
     packet = ofpbuf_clone(ctx->xin->packet);
 
-    key.skb_priority = 0;
-    key.pkt_mark = 0;
-    memset(&key.tunnel, 0, sizeof key.tunnel);
-
     ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
                                           &ctx->xout->odp_actions,
                                           &ctx->xout->wc,
                                           &ctx->mpls_depth_delta);
 
-    odp_execute_actions(NULL, packet, &key, ctx->xout->odp_actions.data,
-                        ctx->xout->odp_actions.size, NULL, NULL);
+    odp_execute_actions(NULL, packet, &md, ctx->xout->odp_actions.data,
+                        ctx->xout->odp_actions.size, NULL);
 
     pin = xmalloc(sizeof *pin);
     pin->up.packet_len = packet->size;
@@ -2496,6 +2524,15 @@ xlate_sample_action(struct xlate_ctx *ctx,
    * the same percentage. */
   uint32_t probability = (os->probability << 16) | os->probability;
 
+  if (!ctx->xbridge->variable_length_userdata) {
+      static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+
+      VLOG_ERR_RL(&rl, "ignoring NXAST_SAMPLE action because datapath "
+                  "lacks support (needs Linux 3.10+ or kernel module from "
+                  "OVS 1.11+)");
+      return;
+  }
+
   ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
                                         &ctx->xout->odp_actions,
                                         &ctx->xout->wc,
@@ -2860,6 +2897,7 @@ xlate_in_init(struct xlate_in *xin, struct ofproto_dpif *ofproto,
     xin->resubmit_hook = NULL;
     xin->report_hook = NULL;
     xin->resubmit_stats = NULL;
+    xin->skip_wildcards = false;
 }
 
 void
@@ -2905,44 +2943,6 @@ xlate_out_copy(struct xlate_out *dst, const struct xlate_out *src)
     ofpbuf_put(&dst->odp_actions, src->odp_actions.data,
                src->odp_actions.size);
 }
-
-/* Returns a reference to the sflow handled associated with ofproto, or NULL if
- * there is none.  The caller is responsible for decrementing the results ref
- * count with dpif_sflow_unref(). */
-struct dpif_sflow *
-xlate_get_sflow(const struct ofproto_dpif *ofproto)
-{
-    struct dpif_sflow *sflow = NULL;
-    struct xbridge *xbridge;
-
-    ovs_rwlock_rdlock(&xlate_rwlock);
-    xbridge = xbridge_lookup(ofproto);
-    if (xbridge) {
-        sflow = dpif_sflow_ref(xbridge->sflow);
-    }
-    ovs_rwlock_unlock(&xlate_rwlock);
-
-    return sflow;
-}
-
-/* Returns a reference to the ipfix handled associated with ofproto, or NULL if
- * there is none.  The caller is responsible for decrementing the results ref
- * count with dpif_ipfix_unref(). */
-struct dpif_ipfix *
-xlate_get_ipfix(const struct ofproto_dpif *ofproto)
-{
-    struct dpif_ipfix *ipfix = NULL;
-    struct xbridge *xbridge;
-
-    ovs_rwlock_rdlock(&xlate_rwlock);
-    xbridge = xbridge_lookup(ofproto);
-    if (xbridge) {
-        ipfix = dpif_ipfix_ref(xbridge->ipfix);
-    }
-    ovs_rwlock_unlock(&xlate_rwlock);
-
-    return ipfix;
-}
 \f
 static struct skb_priority_to_dscp *
 get_skb_priority(const struct xport *xport, uint32_t skb_priority)
@@ -3080,7 +3080,7 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
     wc->masks.nw_frag |= FLOW_NW_FRAG_MASK;
 
     tnl_may_send = tnl_xlate_init(&ctx.base_flow, flow, wc);
-    if (ctx.xbridge->has_netflow) {
+    if (ctx.xbridge->netflow) {
         netflow_mask_wc(flow, wc);
     }
 
@@ -3092,7 +3092,8 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
     ctx.mpls_depth_delta = 0;
 
     if (!xin->ofpacts && !ctx.rule) {
-        rule_dpif_lookup(ctx.xbridge->ofproto, flow, wc, &rule);
+        rule_dpif_lookup(ctx.xbridge->ofproto, flow,
+                         !xin->skip_wildcards ? wc : NULL, &rule);
         if (ctx.xin->resubmit_stats) {
             rule_dpif_credit_stats(rule, ctx.xin->resubmit_stats);
         }
@@ -3108,7 +3109,7 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
         ofpacts = actions->ofpacts;
         ofpacts_len = actions->ofpacts_len;
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     ofpbuf_use_stub(&ctx.stack, ctx.init_stack, sizeof ctx.init_stack);
@@ -3133,18 +3134,25 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
             goto out;
 
         case OFPC_FRAG_REASM:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
 
         case OFPC_FRAG_NX_MATCH:
             /* Nothing to do. */
             break;
 
         case OFPC_INVALID_TTL_TO_CONTROLLER:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
 
     in_port = get_ofp_port(ctx.xbridge, flow->in_port.ofp_port);
+    if (in_port && in_port->is_tunnel && ctx.xin->resubmit_stats) {
+        netdev_vport_inc_rx(in_port->netdev, ctx.xin->resubmit_stats);
+        if (in_port->bfd) {
+            bfd_account_rx(in_port->bfd, ctx.xin->resubmit_stats);
+        }
+    }
+
     special = process_special(&ctx, flow, in_port, ctx.xin->packet);
     if (special) {
         ctx.xout->slow |= special;
@@ -3198,13 +3206,37 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
         ctx.xout->slow |= SLOW_ACTION;
     }
 
+    if (ctx.xin->resubmit_stats) {
+        mirror_update_stats(ctx.xbridge->mbridge, xout->mirrors,
+                            ctx.xin->resubmit_stats->n_packets,
+                            ctx.xin->resubmit_stats->n_bytes);
+
+        if (ctx.xbridge->netflow) {
+            const struct ofpact *ofpacts;
+            size_t ofpacts_len;
+
+            ofpacts_len = actions->ofpacts_len;
+            ofpacts = actions->ofpacts;
+            if (ofpacts_len == 0
+                || ofpacts->type != OFPACT_CONTROLLER
+                || ofpact_next(ofpacts) < ofpact_end(ofpacts, ofpacts_len)) {
+                /* Only update netflow if we don't have controller flow.  We don't
+                 * report NetFlow expiration messages for such facets because they
+                 * are just part of the control logic for the network, not real
+                 * traffic. */
+                netflow_flow_update(ctx.xbridge->netflow, flow,
+                                    xout->nf_output_iface,
+                                    ctx.xin->resubmit_stats);
+            }
+        }
+    }
+
     ofpbuf_uninit(&ctx.stack);
     ofpbuf_uninit(&ctx.action_set);
 
     /* Clear the metadata and register wildcard masks, because we won't
      * use non-header fields as part of the cache. */
-    memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
-    memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
+    flow_wildcards_clear_non_packet_fields(wc);
 
 out:
     rule_actions_unref(actions);
index 7dd3534..982f1a4 100644 (file)
@@ -73,6 +73,12 @@ struct xlate_in {
      * not if we are just revalidating. */
     bool may_learn;
 
+    /* If the caller of xlate_actions() doesn't need the flow_wildcards
+     * contained in struct xlate_out.  'skip_wildcards' can be set to true
+     * disabling the expensive wildcard computation.  When true, 'wc' in struct
+     * xlate_out is undefined and should not be read. */
+    bool skip_wildcards;
+
     /* The rule initiating translation or NULL. If both 'rule' and 'ofpacts'
      * are NULL, xlate_actions() will do the initial rule lookup itself. */
     struct rule_dpif *rule;
@@ -121,8 +127,9 @@ void xlate_ofproto_set(struct ofproto_dpif *, const char *name,
                        struct rule_dpif *no_packet_in_rule,
                        const struct mac_learning *, struct stp *,
                        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)
+                       const struct dpif_ipfix *, const struct netflow *,
+                       enum ofp_config_flags, bool forward_bpdu,
+                       bool has_in_band, bool variable_length_userdata)
     OVS_REQ_WRLOCK(xlate_rwlock);
 void xlate_remove_ofproto(struct ofproto_dpif *) OVS_REQ_WRLOCK(xlate_rwlock);
 
@@ -146,23 +153,20 @@ void xlate_ofport_remove(struct ofport_dpif *) OVS_REQ_WRLOCK(xlate_rwlock);
 int xlate_receive(const struct dpif_backer *, struct ofpbuf *packet,
                   const struct nlattr *key, size_t key_len,
                   struct flow *, enum odp_key_fitness *,
-                  struct ofproto_dpif **, odp_port_t *odp_in_port)
+                  struct ofproto_dpif **, struct dpif_ipfix **,
+                  struct dpif_sflow **, struct netflow **,
+                  odp_port_t *odp_in_port)
     OVS_EXCLUDED(xlate_rwlock);
 
 void xlate_actions(struct xlate_in *, struct xlate_out *)
     OVS_EXCLUDED(xlate_rwlock);
 void xlate_in_init(struct xlate_in *, struct ofproto_dpif *,
-                   const struct flow *, struct rule_dpif *,
-                   uint16_t tcp_flags, const struct ofpbuf *packet);
+                   const struct flow *, struct rule_dpif *, uint16_t tcp_flags,
+                   const struct ofpbuf *packet);
 void xlate_out_uninit(struct xlate_out *);
 void xlate_actions_for_side_effects(struct xlate_in *);
 void xlate_out_copy(struct xlate_out *dst, const struct xlate_out *src);
 
-struct dpif_sflow *xlate_get_sflow(const struct ofproto_dpif *)
-    OVS_EXCLUDED(xlate_rwlock);
-struct dpif_ipfix *xlate_get_ipfix(const struct ofproto_dpif *)
-    OVS_EXCLUDED(xlate_rwlock);
-
 int xlate_send_packet(const struct ofport_dpif *, struct ofpbuf *);
 
 #endif /* ofproto-dpif-xlate.h */
index 5e0a7f0..91ffe23 100644 (file)
@@ -25,6 +25,7 @@
 #include "bond.h"
 #include "bundle.h"
 #include "byte-order.h"
+#include "connectivity.h"
 #include "connmgr.h"
 #include "coverage.h"
 #include "cfm.h"
@@ -41,6 +42,7 @@
 #include "netdev-vport.h"
 #include "netdev.h"
 #include "netlink.h"
+#include "netlink-socket.h"
 #include "nx-match.h"
 #include "odp-util.h"
 #include "odp-execute.h"
@@ -49,7 +51,6 @@
 #include "ofp-actions.h"
 #include "ofp-parse.h"
 #include "ofp-print.h"
-#include "ofproto-dpif-governor.h"
 #include "ofproto-dpif-ipfix.h"
 #include "ofproto-dpif-mirror.h"
 #include "ofproto-dpif-monitor.h"
@@ -57,6 +58,7 @@
 #include "ofproto-dpif-upcall.h"
 #include "ofproto-dpif-xlate.h"
 #include "poll-loop.h"
+#include "seq.h"
 #include "simap.h"
 #include "smap.h"
 #include "timer.h"
 VLOG_DEFINE_THIS_MODULE(ofproto_dpif);
 
 COVERAGE_DEFINE(ofproto_dpif_expired);
-COVERAGE_DEFINE(facet_revalidate);
-COVERAGE_DEFINE(facet_unexpected);
-COVERAGE_DEFINE(facet_create);
-COVERAGE_DEFINE(facet_remove);
-COVERAGE_DEFINE(subfacet_create);
-COVERAGE_DEFINE(subfacet_destroy);
-COVERAGE_DEFINE(subfacet_install_fail);
 COVERAGE_DEFINE(packet_in_overflow);
 
 /* Number of implemented OpenFlow tables. */
@@ -84,24 +79,14 @@ 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;
 
 struct rule_dpif {
     struct rule up;
 
     /* These statistics:
      *
-     *   - Do include packets and bytes from facets that have been deleted or
-     *     whose own statistics have been folded into the rule.
-     *
-     *   - Do include packets and bytes sent "by hand" that were accounted to
-     *     the rule without any facet being involved (this is a rare corner
-     *     case in rule_execute()).
-     *
-     *   - Do not include packet or bytes that can be obtained from any facet's
-     *     packet_count or byte_count member or that can be obtained from the
-     *     datapath by, e.g., dpif_flow_get() for any subfacet.
-     */
+     *   - Do include packets and bytes from datapath flows which have not
+     *   recently been processed by a revalidator. */
     struct ovs_mutex stats_mutex;
     uint64_t packet_count OVS_GUARDED;  /* Number of packets received. */
     uint64_t byte_count OVS_GUARDED;    /* Number of bytes received. */
@@ -109,23 +94,15 @@ struct rule_dpif {
 
 static void rule_get_stats(struct rule *, uint64_t *packets, uint64_t *bytes);
 static struct rule_dpif *rule_dpif_cast(const struct rule *);
+static void rule_expire(struct rule_dpif *);
 
 struct group_dpif {
     struct ofgroup up;
 
     /* These statistics:
      *
-     *   - Do include packets and bytes from facets that have been deleted or
-     *     whose own statistics have been folded into the rule.
-     *
-     *   - Do include packets and bytes sent "by hand" that were accounted to
-     *     the rule without any facet being involved (this is a rare corner
-     *     case in rule_execute()).
-     *
-     *   - Do not include packet or bytes that can be obtained from any facet's
-     *     packet_count or byte_count member or that can be obtained from the
-     *     datapath by, e.g., dpif_flow_get() for any subfacet.
-     */
+     *   - Do include packets and bytes from datapath flows which have not
+     *   recently been processed by a revalidator. */
     struct ovs_mutex stats_mutex;
     uint64_t packet_count OVS_GUARDED;  /* Number of packets received. */
     uint64_t byte_count OVS_GUARDED;    /* Number of bytes received. */
@@ -164,164 +141,6 @@ static void stp_wait(struct ofproto_dpif *ofproto);
 static int set_stp_port(struct ofport *,
                         const struct ofproto_port_stp_settings *);
 
-static void compose_slow_path(const struct ofproto_dpif *, const struct flow *,
-                              enum slow_path_reason,
-                              uint64_t *stub, size_t stub_size,
-                              const struct nlattr **actionsp,
-                              size_t *actions_lenp);
-
-/* A subfacet (see "struct subfacet" below) has three possible installation
- * states:
- *
- *   - SF_NOT_INSTALLED: Not installed in the datapath.  This will only be the
- *     case just after the subfacet is created, just before the subfacet is
- *     destroyed, or if the datapath returns an error when we try to install a
- *     subfacet.
- *
- *   - SF_FAST_PATH: The subfacet's actions are installed in the datapath.
- *
- *   - SF_SLOW_PATH: An action that sends every packet for the subfacet through
- *     ofproto_dpif is installed in the datapath.
- */
-enum subfacet_path {
-    SF_NOT_INSTALLED,           /* No datapath flow for this subfacet. */
-    SF_FAST_PATH,               /* Full actions are installed. */
-    SF_SLOW_PATH,               /* Send-to-userspace action is installed. */
-};
-
-/* A dpif flow and actions associated with a facet.
- *
- * See also the large comment on struct facet. */
-struct subfacet {
-    /* Owners. */
-    struct hmap_node hmap_node; /* In struct ofproto_dpif 'subfacets' list. */
-    struct list list_node;      /* In struct facet's 'facets' list. */
-    struct facet *facet;        /* Owning facet. */
-    struct dpif_backer *backer; /* Owning backer. */
-
-    struct nlattr *key;
-    int key_len;
-
-    long long int used;         /* Time last used; time created if not used. */
-    long long int created;      /* Time created. */
-
-    uint64_t dp_packet_count;   /* Last known packet count in the datapath. */
-    uint64_t dp_byte_count;     /* Last known byte count in the datapath. */
-
-    enum subfacet_path path;    /* Installed in datapath? */
-};
-
-#define SUBFACET_DESTROY_MAX_BATCH 50
-
-static struct subfacet *subfacet_create(struct facet *, struct flow_miss *,
-                                        uint32_t key_hash);
-static struct subfacet *subfacet_find(struct dpif_backer *,
-                                      const struct nlattr *key, size_t key_len,
-                                      uint32_t key_hash);
-static void subfacet_destroy(struct subfacet *);
-static void subfacet_destroy__(struct subfacet *);
-static void subfacet_destroy_batch(struct dpif_backer *,
-                                   struct subfacet **, int n);
-static void subfacet_reset_dp_stats(struct subfacet *,
-                                    struct dpif_flow_stats *);
-static void subfacet_update_stats(struct subfacet *,
-                                  const struct dpif_flow_stats *);
-static int subfacet_install(struct subfacet *,
-                            const struct ofpbuf *odp_actions,
-                            struct dpif_flow_stats *);
-static void subfacet_uninstall(struct subfacet *);
-
-/* A unique, non-overlapping instantiation of an OpenFlow flow.
- *
- * A facet associates a "struct flow", which represents the Open vSwitch
- * userspace idea of an exact-match flow, with one or more subfacets.
- * While the facet is created based on an exact-match flow, it is stored
- * within the ofproto based on the wildcards that could be expressed
- * based on the flow table and other configuration.  (See the 'wc'
- * description in "struct xlate_out" for more details.)
- *
- * Each subfacet tracks the datapath's idea of the flow equivalent to
- * the facet.  When the kernel module (or other dpif implementation) and
- * Open vSwitch userspace agree on the definition of a flow key, there
- * is exactly one subfacet per facet.  If the dpif implementation
- * supports more-specific flow matching than userspace, however, a facet
- * can have more than one subfacet.  Examples include the dpif
- * implementation not supporting the same wildcards as userspace or some
- * distinction in flow that userspace simply doesn't understand.
- *
- * Flow expiration works in terms of subfacets, so a facet must have at
- * least one subfacet or it will never expire, leaking memory. */
-struct facet {
-    /* Owner. */
-    struct ofproto_dpif *ofproto;
-
-    /* Owned data. */
-    struct list subfacets;
-    long long int used;         /* Time last used; time created if not used. */
-
-    /* Key. */
-    struct flow flow;           /* Flow of the creating subfacet. */
-    struct cls_rule cr;         /* In 'ofproto_dpif's facets classifier. */
-
-    /* These statistics:
-     *
-     *   - Do include packets and bytes sent "by hand", e.g. with
-     *     dpif_execute().
-     *
-     *   - Do include packets and bytes that were obtained from the datapath
-     *     when a subfacet's statistics were reset (e.g. dpif_flow_put() with
-     *     DPIF_FP_ZERO_STATS).
-     *
-     *   - Do not include packets or bytes that can be obtained from the
-     *     datapath for any existing subfacet.
-     */
-    uint64_t packet_count;       /* Number of packets received. */
-    uint64_t byte_count;         /* Number of bytes received. */
-
-    /* Resubmit statistics. */
-    uint64_t prev_packet_count;  /* Number of packets from last stats push. */
-    uint64_t prev_byte_count;    /* Number of bytes from last stats push. */
-    long long int prev_used;     /* Used time from last stats push. */
-
-    /* Accounting. */
-    uint64_t accounted_bytes;    /* Bytes processed by facet_account(). */
-    struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */
-    uint16_t tcp_flags;          /* TCP flags seen for this 'rule'. */
-
-    struct xlate_out xout;
-
-    /* Storage for a single subfacet, to reduce malloc() time and space
-     * overhead.  (A facet always has at least one subfacet and in the common
-     * case has exactly one subfacet.  However, 'one_subfacet' may not
-     * always be valid, since it could have been removed after newer
-     * subfacets were pushed onto the 'subfacets' list.) */
-    struct subfacet one_subfacet;
-
-    long long int learn_rl;      /* Rate limiter for facet_learn(). */
-};
-
-static struct facet *facet_create(const struct flow_miss *);
-static void facet_remove(struct facet *);
-static void facet_free(struct facet *);
-
-static struct facet *facet_find(struct ofproto_dpif *, const struct flow *);
-static struct facet *facet_lookup_valid(struct ofproto_dpif *,
-                                        const struct flow *);
-static bool facet_revalidate(struct facet *);
-static bool facet_check_consistency(struct facet *);
-
-static void facet_flush_stats(struct facet *);
-
-static void facet_reset_counters(struct facet *);
-static void flow_push_stats(struct ofproto_dpif *, struct flow *,
-                            struct dpif_flow_stats *, bool may_learn);
-static void facet_push_stats(struct facet *, bool may_learn);
-static void facet_learn(struct facet *);
-static void facet_account(struct facet *);
-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;
@@ -389,16 +208,14 @@ static void port_run(struct ofport_dpif *);
 static int set_bfd(struct ofport *, const struct smap *);
 static int set_cfm(struct ofport *, const struct cfm_settings *);
 static void ofport_update_peer(struct ofport_dpif *);
-static void run_fast_rl(void);
-static int run_fast(struct ofproto *);
 
 struct dpif_completion {
     struct list list_node;
     struct ofoperation *op;
 };
 
-/* Reasons that we might need to revalidate every facet, and corresponding
- * coverage counters.
+/* Reasons that we might need to revalidate every datapath flow, and
+ * corresponding coverage counters.
  *
  * A value of 0 means that there is no need to revalidate.
  *
@@ -412,7 +229,6 @@ enum revalidate_reason {
     REV_PORT_TOGGLED,          /* Port enabled or disabled by CFM, LACP, ...*/
     REV_FLOW_TABLE,            /* Flow table changed. */
     REV_MAC_LEARNING,          /* Mac learning changed. */
-    REV_INCONSISTENCY          /* Facet self-check failed. */
 };
 COVERAGE_DEFINE(rev_reconfigure);
 COVERAGE_DEFINE(rev_stp);
@@ -420,12 +236,6 @@ COVERAGE_DEFINE(rev_bond);
 COVERAGE_DEFINE(rev_port_toggled);
 COVERAGE_DEFINE(rev_flow_table);
 COVERAGE_DEFINE(rev_mac_learning);
-COVERAGE_DEFINE(rev_inconsistency);
-
-struct avg_subfacet_rates {
-    double add_rate;   /* Moving average of new flows created per minute. */
-    double del_rate;   /* Moving average of flows deleted per minute. */
-};
 
 /* All datapaths of a given type share a single dpif backer instance. */
 struct dpif_backer {
@@ -433,48 +243,32 @@ struct dpif_backer {
     int refcount;
     struct dpif *dpif;
     struct udpif *udpif;
-    struct timer next_expiration;
 
     struct ovs_rwlock odp_to_ofport_lock;
-    struct hmap odp_to_ofport_map OVS_GUARDED; /* ODP port to ofport map. */
+    struct hmap odp_to_ofport_map OVS_GUARDED; /* Contains "struct ofport"s. */
 
     struct simap tnl_backers;      /* Set of dpif ports backing tunnels. */
 
-    /* Facet revalidation flags applying to facets which use this backer. */
-    enum revalidate_reason need_revalidate; /* Revalidate every facet. */
+    enum revalidate_reason need_revalidate; /* Revalidate all flows. */
 
-    struct hmap drop_keys; /* Set of dropped odp keys. */
     bool recv_set_enable; /* Enables or disables receiving packets. */
 
-    struct hmap subfacets;
-    struct governor *governor;
-
-    /* Subfacet statistics.
-     *
-     * These keep track of the total number of subfacets added and deleted and
-     * flow life span.  They are useful for computing the flow rates stats
-     * exposed via "ovs-appctl dpif/show".  The goal is to learn about
-     * traffic patterns in ways that we can use later to improve Open vSwitch
-     * performance in new situations.  */
-    long long int created;           /* Time when it is created. */
-    unsigned max_n_subfacet;         /* Maximum number of flows */
-    unsigned avg_n_subfacet;         /* Average number of flows. */
-    long long int avg_subfacet_life; /* Average life span of subfacets. */
-
-    /* Number of upcall handling threads. */
-    unsigned int n_handler_threads;
+    /* True if the datapath supports variable-length
+     * OVS_USERSPACE_ATTR_USERDATA in OVS_ACTION_ATTR_USERSPACE actions.
+     * False if the datapath supports only 8-byte (or shorter) userdata. */
+    bool variable_length_userdata;
 };
 
 /* All existing ofproto_backer instances, indexed by ofproto->up.type. */
 static struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers);
 
-static void drop_key_clear(struct dpif_backer *);
-
 struct ofproto_dpif {
     struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */
     struct ofproto up;
     struct dpif_backer *backer;
 
+    uint64_t dump_seq; /* Last read of udpif_dump_seq(). */
+
     /* 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. */
@@ -487,12 +281,9 @@ struct ofproto_dpif {
     struct hmap bundles;        /* Contains "struct ofbundle"s. */
     struct mac_learning *ml;
     bool has_bonded_bundles;
+    bool lacp_enabled;
     struct mbridge *mbridge;
 
-    /* Facets. */
-    struct classifier facets;     /* Contains 'struct facet's. */
-    long long int consistency_rl;
-
     struct ovs_mutex stats_mutex;
     struct netdev_stats stats OVS_GUARDED; /* To account packets generated and
                                             * consumed in userspace. */
@@ -511,19 +302,12 @@ struct ofproto_dpif {
     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;
+    uint64_t change_seq;           /* Connectivity status changes. */
 
     /* Work queues. */
     struct guarded_list pins;      /* Contains "struct ofputil_packet_in"s. */
 };
 
-/* By default, flows in the datapath are wildcarded (megaflows).  They
- * may be disabled with the "ovs-appctl dpif/disable-megaflows" command. */
-static bool enable_megaflows = true;
-
 /* All existing ofproto_dpif instances, indexed by ->up.name. */
 static struct hmap all_ofproto_dpifs = HMAP_INITIALIZER(&all_ofproto_dpifs);
 
@@ -543,15 +327,6 @@ static void ofproto_trace(struct ofproto_dpif *, const struct flow *,
                           const struct ofpact[], size_t ofpacts_len,
                           struct ds *);
 
-/* Upcalls. */
-static void handle_upcalls(struct dpif_backer *);
-
-/* Flow expiration. */
-static int expire(struct dpif_backer *);
-
-/* NetFlow. */
-static void send_netflow_active_timeouts(struct ofproto_dpif *);
-
 /* Global variables. */
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
@@ -667,7 +442,6 @@ lookup_ofproto_dpif_by_port_name(const char *name)
 static int
 type_run(const char *type)
 {
-    static long long int push_timer = LLONG_MIN;
     struct dpif_backer *backer;
 
     backer = shash_find_data(&all_dpif_backers, type);
@@ -679,16 +453,6 @@ type_run(const char *type)
 
     dpif_run(backer->dpif);
 
-    /* The most natural place to push facet statistics is when they're pulled
-     * from the datapath.  However, when there are many flows in the datapath,
-     * this expensive operation can occur so frequently, that it reduces our
-     * ability to quickly set up flows.  To reduce the cost, we push statistics
-     * here instead. */
-    if (time_msec() > push_timer) {
-        push_timer = time_msec() + 2000;
-        push_all_stats();
-    }
-
     /* If vswitchd started with other_config:flow_restore_wait set as "true",
      * and the configuration has now changed to "false", enable receiving
      * packets from the datapath. */
@@ -699,22 +463,15 @@ type_run(const char *type)
 
         error = dpif_recv_set(backer->dpif, backer->recv_set_enable);
         if (error) {
-            udpif_recv_set(backer->udpif, 0, false);
             VLOG_ERR("Failed to enable receiving packets in dpif.");
             return error;
         }
-        udpif_recv_set(backer->udpif, n_handler_threads,
-                       backer->recv_set_enable);
         dpif_flow_flush(backer->dpif);
         backer->need_revalidate = REV_RECONFIGURE;
     }
 
-    /* If the n_handler_threads is reconfigured, call udpif_recv_set()
-     * to reset the handler threads. */
-    if (backer->n_handler_threads != n_handler_threads) {
-        udpif_recv_set(backer->udpif, n_handler_threads,
-                       backer->recv_set_enable);
-        backer->n_handler_threads = n_handler_threads;
+    if (backer->recv_set_enable) {
+        udpif_set_threads(backer->udpif, n_handlers, n_revalidators);
     }
 
     if (backer->need_revalidate) {
@@ -782,18 +539,11 @@ type_run(const char *type)
         case REV_PORT_TOGGLED:  COVERAGE_INC(rev_port_toggled);  break;
         case REV_FLOW_TABLE:    COVERAGE_INC(rev_flow_table);    break;
         case REV_MAC_LEARNING:  COVERAGE_INC(rev_mac_learning);  break;
-        case REV_INCONSISTENCY: COVERAGE_INC(rev_inconsistency); break;
         }
         backer->need_revalidate = 0;
 
-        /* Clear the drop_keys in case we should now be accepting some
-         * formerly dropped flows. */
-        drop_key_clear(backer);
-
         HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
-            struct facet *facet, *next;
             struct ofport_dpif *ofport;
-            struct cls_cursor cursor;
             struct ofbundle *bundle;
 
             if (ofproto->backer != backer) {
@@ -806,10 +556,10 @@ type_run(const char *type)
                               ofproto->no_packet_in_rule, ofproto->ml,
                               ofproto->stp, ofproto->mbridge,
                               ofproto->sflow, ofproto->ipfix,
-                              ofproto->up.frag_handling,
+                              ofproto->netflow, ofproto->up.frag_handling,
                               ofproto->up.forward_bpdu,
                               connmgr_has_in_band(ofproto->up.connmgr),
-                              ofproto->netflow != NULL);
+                              ofproto->backer->variable_length_userdata);
 
             HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
                 xlate_bundle_set(ofproto, bundle, bundle->name,
@@ -832,49 +582,13 @@ type_run(const char *type)
                                  ofport->is_tunnel, ofport->may_enable);
             }
             ovs_rwlock_unlock(&xlate_rwlock);
-
-            /* Only ofproto-dpif cares about the facet classifier so we just
-             * lock cls_cursor_init() to appease the thread safety analysis. */
-            ovs_rwlock_rdlock(&ofproto->facets.rwlock);
-            cls_cursor_init(&cursor, &ofproto->facets, NULL);
-            ovs_rwlock_unlock(&ofproto->facets.rwlock);
-            CLS_CURSOR_FOR_EACH_SAFE (facet, next, cr, &cursor) {
-                facet_revalidate(facet);
-                run_fast_rl();
-            }
         }
 
         udpif_revalidate(backer->udpif);
     }
 
-    if (!backer->recv_set_enable) {
-        /* Wake up before a max of 1000ms. */
-        timer_set_duration(&backer->next_expiration, 1000);
-    } else if (timer_expired(&backer->next_expiration)) {
-        int delay = expire(backer);
-        timer_set_duration(&backer->next_expiration, delay);
-    }
-
     process_dpif_port_changes(backer);
 
-    if (backer->governor) {
-        size_t n_subfacets;
-
-        governor_run(backer->governor);
-
-        /* If the governor has shrunk to its minimum size and the number of
-         * subfacets has dwindled, then drop the governor entirely.
-         *
-         * For hysteresis, the number of subfacets to drop the governor is
-         * smaller than the number needed to trigger its creation. */
-        n_subfacets = hmap_count(&backer->subfacets);
-        if (n_subfacets * 4 < flow_eviction_threshold
-            && governor_is_idle(backer->governor)) {
-            governor_destroy(backer->governor);
-            backer->governor = NULL;
-        }
-    }
-
     return 0;
 }
 
@@ -1007,44 +721,6 @@ process_dpif_port_error(struct dpif_backer *backer, int error)
     }
 }
 
-static int
-dpif_backer_run_fast(struct dpif_backer *backer)
-{
-    handle_upcalls(backer);
-
-    return 0;
-}
-
-static int
-type_run_fast(const char *type)
-{
-    struct dpif_backer *backer;
-
-    backer = shash_find_data(&all_dpif_backers, type);
-    if (!backer) {
-        /* This is not necessarily a problem, since backers are only
-         * created on demand. */
-        return 0;
-    }
-
-    return dpif_backer_run_fast(backer);
-}
-
-static void
-run_fast_rl(void)
-{
-    static long long int port_rl = LLONG_MIN;
-
-    if (time_msec() >= port_rl) {
-        struct ofproto_dpif *ofproto;
-
-        HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
-            run_fast(&ofproto->up);
-        }
-        port_rl = time_msec() + 200;
-    }
-}
-
 static void
 type_wait(const char *type)
 {
@@ -1057,13 +733,7 @@ type_wait(const char *type)
         return;
     }
 
-    if (backer->governor) {
-        governor_wait(backer->governor);
-    }
-
-    timer_wait(&backer->next_expiration);
     dpif_wait(backer->dpif);
-    udpif_wait(backer->udpif);
 }
 \f
 /* Basic life-cycle. */
@@ -1093,9 +763,6 @@ close_dpif_backer(struct dpif_backer *backer)
         return;
     }
 
-    drop_key_clear(backer);
-    hmap_destroy(&backer->drop_keys);
-
     udpif_destroy(backer->udpif);
 
     simap_destroy(&backer->tnl_backers);
@@ -1105,10 +772,6 @@ close_dpif_backer(struct dpif_backer *backer)
     free(backer->type);
     dpif_close(backer->dpif);
 
-    ovs_assert(hmap_is_empty(&backer->subfacets));
-    hmap_destroy(&backer->subfacets);
-    governor_destroy(backer->governor);
-
     free(backer);
 }
 
@@ -1118,6 +781,8 @@ struct odp_garbage {
     odp_port_t odp_port;
 };
 
+static bool check_variable_length_userdata(struct dpif_backer *backer);
+
 static int
 open_dpif_backer(const char *type, struct dpif_backer **backerp)
 {
@@ -1175,13 +840,9 @@ open_dpif_backer(const char *type, struct dpif_backer **backerp)
     backer->udpif = udpif_create(backer, backer->dpif);
 
     backer->type = xstrdup(type);
-    backer->governor = NULL;
     backer->refcount = 1;
     hmap_init(&backer->odp_to_ofport_map);
     ovs_rwlock_init(&backer->odp_to_ofport_lock);
-    hmap_init(&backer->drop_keys);
-    hmap_init(&backer->subfacets);
-    timer_set_duration(&backer->next_expiration, 1000);
     backer->need_revalidate = 0;
     simap_init(&backer->tnl_backers);
     backer->recv_set_enable = !ofproto_get_flow_restore_wait();
@@ -1220,18 +881,91 @@ open_dpif_backer(const char *type, struct dpif_backer **backerp)
         close_dpif_backer(backer);
         return error;
     }
-    udpif_recv_set(backer->udpif, n_handler_threads,
-                   backer->recv_set_enable);
-    backer->n_handler_threads = n_handler_threads;
+    backer->variable_length_userdata = check_variable_length_userdata(backer);
 
-    backer->max_n_subfacet = 0;
-    backer->created = time_msec();
-    backer->avg_n_subfacet = 0;
-    backer->avg_subfacet_life = 0;
+    if (backer->recv_set_enable) {
+        udpif_set_threads(backer->udpif, n_handlers, n_revalidators);
+    }
 
     return error;
 }
 
+/* Tests whether 'backer''s datapath supports variable-length
+ * OVS_USERSPACE_ATTR_USERDATA in OVS_ACTION_ATTR_USERSPACE actions.  We need
+ * to disable some features on older datapaths that don't support this
+ * feature.
+ *
+ * Returns false if 'backer' definitely does not support variable-length
+ * userdata, true if it seems to support them or if at least the error we get
+ * is ambiguous. */
+static bool
+check_variable_length_userdata(struct dpif_backer *backer)
+{
+    struct eth_header *eth;
+    struct ofpbuf actions;
+    struct dpif_execute execute;
+    struct ofpbuf packet;
+    size_t start;
+    int error;
+
+    /* Compose a userspace action that will cause an ERANGE error on older
+     * datapaths that don't support variable-length userdata.
+     *
+     * We really test for using userdata longer than 8 bytes, but older
+     * datapaths accepted these, silently truncating the userdata to 8 bytes.
+     * The same older datapaths rejected userdata shorter than 8 bytes, so we
+     * test for that instead as a proxy for longer userdata support. */
+    ofpbuf_init(&actions, 64);
+    start = nl_msg_start_nested(&actions, OVS_ACTION_ATTR_USERSPACE);
+    nl_msg_put_u32(&actions, OVS_USERSPACE_ATTR_PID,
+                   dpif_port_get_pid(backer->dpif, ODPP_NONE));
+    nl_msg_put_unspec_zero(&actions, OVS_USERSPACE_ATTR_USERDATA, 4);
+    nl_msg_end_nested(&actions, start);
+
+    /* Compose a dummy ethernet packet. */
+    ofpbuf_init(&packet, ETH_HEADER_LEN);
+    eth = ofpbuf_put_zeros(&packet, ETH_HEADER_LEN);
+    eth->eth_type = htons(0x1234);
+
+    /* Execute the actions.  On older datapaths this fails with ERANGE, on
+     * newer datapaths it succeeds. */
+    execute.actions = actions.data;
+    execute.actions_len = actions.size;
+    execute.packet = &packet;
+    execute.md = PKT_METADATA_INITIALIZER(0);
+    execute.needs_help = false;
+
+    error = dpif_execute(backer->dpif, &execute);
+
+    ofpbuf_uninit(&packet);
+    ofpbuf_uninit(&actions);
+
+    switch (error) {
+    case 0:
+        /* Variable-length userdata is supported.
+         *
+         * Purge received packets to avoid processing the nonsense packet we
+         * sent to userspace, then report success. */
+        dpif_recv_purge(backer->dpif);
+        return true;
+
+    case ERANGE:
+        /* Variable-length userdata is not supported. */
+        VLOG_WARN("%s: datapath does not support variable-length userdata "
+                  "feature (needs Linux 3.10+ or kernel module from OVS "
+                  "1..11+).  The NXAST_SAMPLE action will be ignored.",
+                  dpif_name(backer->dpif));
+        return false;
+
+    default:
+        /* Something odd happened.  We're not sure whether variable-length
+         * userdata is supported.  Default to "yes". */
+        VLOG_WARN("%s: variable-length userdata feature probe failed (%s)",
+                  dpif_name(backer->dpif), ovs_strerror(error));
+        return true;
+    }
+}
+
 static int
 construct(struct ofproto *ofproto_)
 {
@@ -1248,16 +982,15 @@ construct(struct ofproto *ofproto_)
     ofproto->sflow = NULL;
     ofproto->ipfix = NULL;
     ofproto->stp = NULL;
+    ofproto->dump_seq = 0;
     hmap_init(&ofproto->bundles);
     ofproto->ml = mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME);
     ofproto->mbridge = mbridge_create();
     ofproto->has_bonded_bundles = false;
+    ofproto->lacp_enabled = false;
     ovs_mutex_init(&ofproto->stats_mutex);
     ovs_mutex_init(&ofproto->vsp_mutex);
 
-    classifier_init(&ofproto->facets, NULL);
-    ofproto->consistency_rl = LLONG_MIN;
-
     guarded_list_init(&ofproto->pins);
 
     ofproto_dpif_unixctl_init();
@@ -1269,6 +1002,7 @@ construct(struct ofproto *ofproto_)
     sset_init(&ofproto->ghost_ports);
     sset_init(&ofproto->port_poll_set);
     ofproto->port_poll_errno = 0;
+    ofproto->change_seq = 0;
 
     SHASH_FOR_EACH_SAFE (node, next, &init_ofp_ports) {
         struct iface_hint *iface_hint = node->data;
@@ -1294,9 +1028,6 @@ construct(struct ofproto *ofproto_)
     error = add_internal_flows(ofproto);
     ofproto->up.tables[TBL_INTERNAL].flags = OFTABLE_HIDDEN | OFTABLE_READONLY;
 
-    ofproto->n_hit = 0;
-    ofproto->n_missed = 0;
-
     return error;
 }
 
@@ -1335,7 +1066,7 @@ add_internal_flow(struct ofproto_dpif *ofproto, int id,
                                   rulep)) {
         rule_dpif_unref(*rulep);
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     return 0;
@@ -1382,18 +1113,9 @@ destruct(struct ofproto *ofproto_)
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct rule_dpif *rule, *next_rule;
     struct ofproto_packet_in *pin, *next_pin;
-    struct facet *facet, *next_facet;
-    struct cls_cursor cursor;
     struct oftable *table;
     struct list pins;
 
-    ovs_rwlock_rdlock(&ofproto->facets.rwlock);
-    cls_cursor_init(&cursor, &ofproto->facets, NULL);
-    ovs_rwlock_unlock(&ofproto->facets.rwlock);
-    CLS_CURSOR_FOR_EACH_SAFE (facet, next_facet, cr, &cursor) {
-        facet_remove(facet);
-    }
-
     ofproto->backer->need_revalidate = REV_RECONFIGURE;
     ovs_rwlock_wrlock(&xlate_rwlock);
     xlate_remove_ofproto(ofproto);
@@ -1426,13 +1148,11 @@ destruct(struct ofproto *ofproto_)
 
     mbridge_unref(ofproto->mbridge);
 
-    netflow_destroy(ofproto->netflow);
+    netflow_unref(ofproto->netflow);
     dpif_sflow_unref(ofproto->sflow);
     hmap_destroy(&ofproto->bundles);
     mac_learning_unref(ofproto->ml);
 
-    classifier_destroy(&ofproto->facets);
-
     hmap_destroy(&ofproto->vlandev_map);
     hmap_destroy(&ofproto->realdev_vid_map);
 
@@ -1446,37 +1166,11 @@ destruct(struct ofproto *ofproto_)
     close_dpif_backer(ofproto->backer);
 }
 
-static int
-run_fast(struct ofproto *ofproto_)
-{
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    struct ofproto_packet_in *pin, *next_pin;
-    struct list pins;
-
-    /* Do not perform any periodic activity required by 'ofproto' while
-     * waiting for flow restore to complete. */
-    if (ofproto_get_flow_restore_wait()) {
-        return 0;
-    }
-
-    guarded_list_pop_all(&ofproto->pins, &pins);
-    LIST_FOR_EACH_SAFE (pin, next_pin, list_node, &pins) {
-        connmgr_send_packet_in(ofproto->up.connmgr, pin);
-        list_remove(&pin->list_node);
-        free(CONST_CAST(void *, pin->up.packet));
-        free(pin);
-    }
-
-    return 0;
-}
-
 static int
 run(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    struct ofport_dpif *ofport;
-    struct ofbundle *bundle;
-    int error;
+    uint64_t new_seq, new_dump_seq;
 
     if (mbridge_need_revalidate(ofproto->mbridge)) {
         ofproto->backer->need_revalidate = REV_RECONFIGURE;
@@ -1485,21 +1179,23 @@ run(struct ofproto *ofproto_)
         ovs_rwlock_unlock(&ofproto->ml->rwlock);
     }
 
-    /* Do not perform any periodic activity below required by 'ofproto' while
+    /* Do not perform any periodic activity required by 'ofproto' while
      * waiting for flow restore to complete. */
-    if (ofproto_get_flow_restore_wait()) {
-        return 0;
-    }
+    if (!ofproto_get_flow_restore_wait()) {
+        struct ofproto_packet_in *pin, *next_pin;
+        struct list pins;
 
-    error = run_fast(ofproto_);
-    if (error) {
-        return error;
+        guarded_list_pop_all(&ofproto->pins, &pins);
+        LIST_FOR_EACH_SAFE (pin, next_pin, list_node, &pins) {
+            connmgr_send_packet_in(ofproto->up.connmgr, pin);
+            list_remove(&pin->list_node);
+            free(CONST_CAST(void *, pin->up.packet));
+            free(pin);
+        }
     }
 
     if (ofproto->netflow) {
-        if (netflow_run(ofproto->netflow)) {
-            send_netflow_active_timeouts(ofproto);
-        }
+        netflow_run(ofproto->netflow);
     }
     if (ofproto->sflow) {
         dpif_sflow_run(ofproto->sflow);
@@ -1508,11 +1204,22 @@ run(struct ofproto *ofproto_)
         dpif_ipfix_run(ofproto->ipfix);
     }
 
-    HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
-        port_run(ofport);
+    new_seq = seq_read(connectivity_seq_get());
+    if (ofproto->change_seq != new_seq) {
+        struct ofport_dpif *ofport;
+
+        HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
+            port_run(ofport);
+        }
+
+        ofproto->change_seq = new_seq;
     }
-    HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
-        bundle_run(bundle);
+    if (ofproto->lacp_enabled || ofproto->has_bonded_bundles) {
+        struct ofbundle *bundle;
+
+        HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
+            bundle_run(bundle);
+        }
     }
 
     stp_run(ofproto);
@@ -1522,28 +1229,35 @@ run(struct ofproto *ofproto_)
     }
     ovs_rwlock_unlock(&ofproto->ml->rwlock);
 
-    /* Check the consistency of a random facet, to aid debugging. */
-    ovs_rwlock_rdlock(&ofproto->facets.rwlock);
-    if (time_msec() >= ofproto->consistency_rl
-        && !classifier_is_empty(&ofproto->facets)
-        && !ofproto->backer->need_revalidate) {
-        struct cls_subtable *table;
-        struct cls_rule *cr;
-        struct facet *facet;
-
-        ofproto->consistency_rl = time_msec() + 250;
-
-        table = CONTAINER_OF(hmap_random_node(&ofproto->facets.subtables),
-                             struct cls_subtable, hmap_node);
-        cr = CONTAINER_OF(hmap_random_node(&table->rules), struct cls_rule,
-                          hmap_node);
-        facet = CONTAINER_OF(cr, struct facet, cr);
-
-        if (!facet_check_consistency(facet)) {
-            ofproto->backer->need_revalidate = REV_INCONSISTENCY;
+    new_dump_seq = seq_read(udpif_dump_seq(ofproto->backer->udpif));
+    if (ofproto->dump_seq != new_dump_seq) {
+        struct rule *rule, *next_rule;
+
+        /* We know stats are relatively fresh, so now is a good time to do some
+         * periodic work. */
+        ofproto->dump_seq = new_dump_seq;
+
+        /* Expire OpenFlow flows whose idle_timeout or hard_timeout
+         * has passed. */
+        ovs_mutex_lock(&ofproto_mutex);
+        LIST_FOR_EACH_SAFE (rule, next_rule, expirable,
+                            &ofproto->up.expirable) {
+            rule_expire(rule_dpif_cast(rule));
+        }
+        ovs_mutex_unlock(&ofproto_mutex);
+
+        /* All outstanding data in existing flows has been accounted, so it's a
+         * good time to do bond rebalancing. */
+        if (ofproto->has_bonded_bundles) {
+            struct ofbundle *bundle;
+
+            HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
+                if (bundle->bond) {
+                    bond_rebalance(bundle->bond);
+                }
+            }
         }
     }
-    ovs_rwlock_unlock(&ofproto->facets.rwlock);
 
     return 0;
 }
@@ -1552,7 +1266,6 @@ static void
 wait(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    struct ofbundle *bundle;
 
     if (ofproto_get_flow_restore_wait()) {
         return;
@@ -1564,8 +1277,12 @@ wait(struct ofproto *ofproto_)
     if (ofproto->ipfix) {
         dpif_ipfix_wait(ofproto->ipfix);
     }
-    HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
-        bundle_wait(bundle);
+    if (ofproto->lacp_enabled || ofproto->has_bonded_bundles) {
+        struct ofbundle *bundle;
+
+        HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
+            bundle_wait(bundle);
+        }
     }
     if (ofproto->netflow) {
         netflow_wait(ofproto->netflow);
@@ -1579,58 +1296,25 @@ wait(struct ofproto *ofproto_)
         VLOG_DBG_RL(&rl, "need revalidate in ofproto_wait_cb()");
         poll_immediate_wake();
     }
+
+    seq_wait(udpif_dump_seq(ofproto->backer->udpif), ofproto->dump_seq);
 }
 
 static void
-get_memory_usage(const struct ofproto *ofproto_, struct simap *usage)
+type_get_memory_usage(const char *type, struct simap *usage)
 {
-    const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    struct cls_cursor cursor;
-    size_t n_subfacets = 0;
-    struct facet *facet;
-
-    ovs_rwlock_rdlock(&ofproto->facets.rwlock);
-    simap_increase(usage, "facets", classifier_count(&ofproto->facets));
-    ovs_rwlock_unlock(&ofproto->facets.rwlock);
+    struct dpif_backer *backer;
 
-    ovs_rwlock_rdlock(&ofproto->facets.rwlock);
-    cls_cursor_init(&cursor, &ofproto->facets, NULL);
-    CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
-        n_subfacets += list_size(&facet->subfacets);
+    backer = shash_find_data(&all_dpif_backers, type);
+    if (backer) {
+        udpif_get_memory_usage(backer->udpif, usage);
     }
-    ovs_rwlock_unlock(&ofproto->facets.rwlock);
-    simap_increase(usage, "subfacets", n_subfacets);
 }
 
 static void
-flush(struct ofproto *ofproto_)
+flush(struct ofproto *ofproto OVS_UNUSED)
 {
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    struct subfacet *subfacet, *next_subfacet;
-    struct subfacet *batch[SUBFACET_DESTROY_MAX_BATCH];
-    int n_batch;
-
-    n_batch = 0;
-    HMAP_FOR_EACH_SAFE (subfacet, next_subfacet, hmap_node,
-                        &ofproto->backer->subfacets) {
-        if (subfacet->facet->ofproto != ofproto) {
-            continue;
-        }
-
-        if (subfacet->path != SF_NOT_INSTALLED) {
-            batch[n_batch++] = subfacet;
-            if (n_batch >= SUBFACET_DESTROY_MAX_BATCH) {
-                subfacet_destroy_batch(ofproto->backer, batch, n_batch);
-                n_batch = 0;
-            }
-        } else {
-            subfacet_destroy(subfacet);
-        }
-    }
-
-    if (n_batch > 0) {
-        subfacet_destroy_batch(ofproto->backer, batch, n_batch);
-    }
+    udpif_flush();
 }
 
 static void
@@ -2179,6 +1863,24 @@ get_stp_port_status(struct ofport *ofport_,
     s->state = stp_port_get_state(sp);
     s->sec_in_state = (time_msec() - ofport->stp_state_entered) / 1000;
     s->role = stp_port_get_role(sp);
+
+    return 0;
+}
+
+static int
+get_stp_port_stats(struct ofport *ofport_,
+                   struct ofproto_port_stp_stats *s)
+{
+    struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+    struct stp_port *sp = ofport->stp_port;
+
+    if (!ofproto->stp || !sp) {
+        s->enabled = false;
+        return 0;
+    }
+
+    s->enabled = true;
     stp_port_get_counts(sp, &s->tx_count, &s->rx_count, &s->error_count);
 
     return 0;
@@ -2449,6 +2151,7 @@ bundle_set(struct ofproto *ofproto_, void *aux,
 
     /* LACP. */
     if (s->lacp) {
+        ofproto->lacp_enabled = true;
         if (!bundle->lacp) {
             ofproto->backer->need_revalidate = REV_RECONFIGURE;
             bundle->lacp = lacp_create();
@@ -2534,7 +2237,7 @@ bundle_set(struct ofproto *ofproto_, void *aux,
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
     if (!vlan_bitmap_equal(trunks, bundle->trunks)) {
         free(bundle->trunks);
@@ -2744,7 +2447,6 @@ static int
 mirror_get_stats__(struct ofproto *ofproto, void *aux,
                    uint64_t *packets, uint64_t *bytes)
 {
-    push_all_stats();
     return mirror_get_stats(ofproto_dpif_cast(ofproto)->mbridge, aux, packets,
                             bytes);
 }
@@ -2796,13 +2498,6 @@ get_ofp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
     return ofport ? ofport_dpif_cast(ofport) : NULL;
 }
 
-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);
-    return port && &ofproto->up == port->up.ofproto ? port : NULL;
-}
-
 static void
 ofproto_port_from_dpif_port(struct ofproto_dpif *ofproto,
                             struct ofproto_port *ofproto_port,
@@ -3015,8 +2710,6 @@ port_get_stats(const struct ofport *ofport_, struct netdev_stats *stats)
     struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
     int error;
 
-    push_all_stats();
-
     error = netdev_get_stats(ofport->up.netdev, stats);
 
     if (!error && ofport_->ofp_port == OFPP_LOCAL) {
@@ -3153,699 +2846,36 @@ port_is_lacp_current(const struct ofport *ofport_)
             : -1);
 }
 \f
-/* Upcall handling. */
-
-struct flow_miss_op {
-    struct dpif_op dpif_op;
-
-    uint64_t slow_stub[128 / 8]; /* Buffer for compose_slow_path() */
-    struct xlate_out xout;
-    bool xout_garbage;           /* 'xout' needs to be uninitialized? */
-
-    struct ofpbuf mask;          /* Flow mask for "put" ops. */
-    struct odputil_keybuf maskbuf;
-
-    /* If this is a "put" op, then a pointer to the subfacet that should
-     * be marked as uninstalled if the operation fails. */
-    struct subfacet *subfacet;
-};
-
-/* Figures out whether a flow that missed in 'ofproto', whose details are in
- * 'miss' masked by 'wc', is likely to be worth tracking in detail in userspace
- * and (usually) installing a datapath flow.  The answer is usually "yes" (a
- * return value of true).  However, for short flows the cost of bookkeeping is
- * much higher than the benefits, so when the datapath holds a large number of
- * flows we impose some heuristics to decide which flows are likely to be worth
- * tracking. */
-static bool
-flow_miss_should_make_facet(struct flow_miss *miss)
+/* If 'rule' is an OpenFlow rule, that has expired according to OpenFlow rules,
+ * then delete it entirely. */
+static void
+rule_expire(struct rule_dpif *rule)
+    OVS_REQUIRES(ofproto_mutex)
 {
-    struct dpif_backer *backer = miss->ofproto->backer;
-    uint32_t hash;
+    uint16_t idle_timeout, hard_timeout;
+    long long int now = time_msec();
+    int reason;
 
-    switch (flow_miss_model) {
-    case OFPROTO_HANDLE_MISS_AUTO:
-        break;
-    case OFPROTO_HANDLE_MISS_WITH_FACETS:
-        return true;
-    case OFPROTO_HANDLE_MISS_WITHOUT_FACETS:
-        return false;
+    ovs_assert(!rule->up.pending);
+
+    /* Has 'rule' expired? */
+    ovs_mutex_lock(&rule->up.mutex);
+    hard_timeout = rule->up.hard_timeout;
+    idle_timeout = rule->up.idle_timeout;
+    if (hard_timeout && now > rule->up.modified + hard_timeout * 1000) {
+        reason = OFPRR_HARD_TIMEOUT;
+    } else if (idle_timeout && now > rule->up.used + idle_timeout * 1000) {
+        reason = OFPRR_IDLE_TIMEOUT;
+    } else {
+        reason = -1;
     }
-
-    if (!backer->governor) {
-        size_t n_subfacets;
-
-        n_subfacets = hmap_count(&backer->subfacets);
-        if (n_subfacets * 2 <= flow_eviction_threshold) {
-            return true;
-        }
-
-        backer->governor = governor_create();
-    }
-
-    hash = flow_hash_in_wildcards(&miss->flow, &miss->xout.wc, 0);
-    return governor_should_install_flow(backer->governor, hash,
-                                        miss->stats.n_packets);
-}
-
-/* Handles 'miss', which matches 'facet'.  May add any required datapath
- * operations to 'ops', incrementing '*n_ops' for each new op.
- *
- * All of the packets in 'miss' are considered to have arrived at time
- * 'miss->stats.used'.  This is really important only for new facets: if we
- * just called time_msec() here, then the new subfacet or its packets could
- * look (occasionally) as though it was used some time after the facet was
- * used.  That can make a one-packet flow look like it has a nonzero duration,
- * which looks odd in e.g. NetFlow statistics. */
-static void
-handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
-                            struct flow_miss_op *ops, size_t *n_ops)
-{
-    enum subfacet_path want_path;
-    struct subfacet *subfacet;
-    uint32_t key_hash;
-
-    /* Update facet stats. */
-    facet->packet_count += miss->stats.n_packets;
-    facet->prev_packet_count += miss->stats.n_packets;
-    facet->byte_count += miss->stats.n_bytes;
-    facet->prev_byte_count += miss->stats.n_bytes;
-
-    /* Look for an existing subfacet.  If we find one, update its used time. */
-    key_hash = odp_flow_key_hash(miss->key, miss->key_len);
-    if (!list_is_empty(&facet->subfacets)) {
-        subfacet = subfacet_find(miss->ofproto->backer,
-                                 miss->key, miss->key_len, key_hash);
-        if (subfacet) {
-            if (subfacet->facet == facet) {
-                subfacet->used = MAX(subfacet->used, miss->stats.used);
-            } else {
-                /* This shouldn't happen. */
-                VLOG_ERR_RL(&rl, "subfacet with wrong facet");
-                subfacet_destroy(subfacet);
-                subfacet = NULL;
-            }
-        }
-    } else {
-        subfacet = NULL;
-    }
-
-    /* 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)) {
-        return;
-    }
-
-    /* Create a subfacet, if we don't already have one. */
-    if (!subfacet) {
-        subfacet = subfacet_create(facet, miss, key_hash);
-    }
-
-    /* Install the subfacet, if it's not already installed. */
-    want_path = facet->xout.slow ? SF_SLOW_PATH : SF_FAST_PATH;
-    if (subfacet->path != want_path) {
-        struct flow_miss_op *op = &ops[(*n_ops)++];
-        struct dpif_flow_put *put = &op->dpif_op.u.flow_put;
-
-        subfacet->path = want_path;
-
-        ofpbuf_use_stack(&op->mask, &op->maskbuf, sizeof op->maskbuf);
-        if (enable_megaflows) {
-            odp_flow_key_from_mask(&op->mask, &facet->xout.wc.masks,
-                                   &miss->flow, UINT32_MAX);
-        }
-
-        op->xout_garbage = false;
-        op->dpif_op.type = DPIF_OP_FLOW_PUT;
-        op->subfacet = subfacet;
-        put->flags = DPIF_FP_CREATE;
-        put->key = miss->key;
-        put->key_len = miss->key_len;
-        put->mask = op->mask.data;
-        put->mask_len = op->mask.size;
-
-        if (want_path == SF_FAST_PATH) {
-            put->actions = facet->xout.odp_actions.data;
-            put->actions_len = facet->xout.odp_actions.size;
-        } else {
-            compose_slow_path(facet->ofproto, &miss->flow, facet->xout.slow,
-                              op->slow_stub, sizeof op->slow_stub,
-                              &put->actions, &put->actions_len);
-        }
-        put->stats = NULL;
-    }
-}
-
-/* Handles flow miss 'miss'.  May add any required datapath operations
- * to 'ops', incrementing '*n_ops' for each new op. */
-static void
-handle_flow_miss(struct flow_miss *miss, struct flow_miss_op *ops,
-                 size_t *n_ops)
-{
-    struct facet *facet;
-
-    miss->ofproto->n_missed += miss->stats.n_packets;
-
-    facet = facet_lookup_valid(miss->ofproto, &miss->flow);
-    if (!facet) {
-        /* There does not exist a bijection between 'struct flow' and datapath
-         * flow keys with fitness ODP_FIT_TO_LITTLE.  This breaks a fundamental
-         * assumption used throughout the facet and subfacet handling code.
-         * Since we have to handle these misses in userspace anyway, we simply
-         * skip facet creation, avoiding the problem altogether. */
-        if (miss->key_fitness == ODP_FIT_TOO_LITTLE
-            || !flow_miss_should_make_facet(miss)) {
-            return;
-        }
-
-        facet = facet_create(miss);
-    }
-    handle_flow_miss_with_facet(miss, facet, ops, n_ops);
-}
-
-static struct drop_key *
-drop_key_lookup(const struct dpif_backer *backer, const struct nlattr *key,
-                size_t key_len)
-{
-    struct drop_key *drop_key;
-
-    HMAP_FOR_EACH_WITH_HASH (drop_key, hmap_node, hash_bytes(key, key_len, 0),
-                             &backer->drop_keys) {
-        if (drop_key->key_len == key_len
-            && !memcmp(drop_key->key, key, key_len)) {
-            return drop_key;
-        }
-    }
-    return NULL;
-}
-
-static void
-drop_key_clear(struct dpif_backer *backer)
-{
-    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 15);
-    struct drop_key *drop_key, *next;
-
-    HMAP_FOR_EACH_SAFE (drop_key, next, hmap_node, &backer->drop_keys) {
-        int error;
-
-        error = dpif_flow_del(backer->dpif, drop_key->key, drop_key->key_len,
-                              NULL);
-        if (error && !VLOG_DROP_WARN(&rl)) {
-            struct ds ds = DS_EMPTY_INITIALIZER;
-            odp_flow_key_format(drop_key->key, drop_key->key_len, &ds);
-            VLOG_WARN("Failed to delete drop key (%s) (%s)",
-                      ovs_strerror(error), ds_cstr(&ds));
-            ds_destroy(&ds);
-        }
-
-        hmap_remove(&backer->drop_keys, &drop_key->hmap_node);
-        drop_key_destroy(drop_key);
-    }
-
-    udpif_drop_key_clear(backer->udpif);
-}
-
-static void
-handle_flow_misses(struct dpif_backer *backer, struct flow_miss_batch *fmb)
-{
-    struct flow_miss_op flow_miss_ops[FLOW_MISS_MAX_BATCH];
-    struct dpif_op *dpif_ops[FLOW_MISS_MAX_BATCH];
-    struct flow_miss *miss;
-    size_t n_ops, i;
-
-    /* Process each element in the to-do list, constructing the set of
-     * operations to batch. */
-    n_ops = 0;
-    HMAP_FOR_EACH (miss, hmap_node, &fmb->misses) {
-        handle_flow_miss(miss, flow_miss_ops, &n_ops);
-    }
-    ovs_assert(n_ops <= ARRAY_SIZE(flow_miss_ops));
-
-    /* Execute batch. */
-    for (i = 0; i < n_ops; i++) {
-        dpif_ops[i] = &flow_miss_ops[i].dpif_op;
-    }
-    dpif_operate(backer->dpif, dpif_ops, n_ops);
-
-    for (i = 0; i < n_ops; i++) {
-        if (dpif_ops[i]->error != 0
-            && flow_miss_ops[i].dpif_op.type == DPIF_OP_FLOW_PUT
-            && flow_miss_ops[i].subfacet) {
-            struct subfacet *subfacet = flow_miss_ops[i].subfacet;
-
-            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;
-        }
-    }
-}
-
-static void
-handle_upcalls(struct dpif_backer *backer)
-{
-    struct flow_miss_batch *fmb;
-    int n_processed;
-
-    for (n_processed = 0; n_processed < FLOW_MISS_MAX_BATCH; n_processed++) {
-        struct drop_key *drop_key = drop_key_next(backer->udpif);
-        if (!drop_key) {
-            break;
-        }
-
-        if (!drop_key_lookup(backer, drop_key->key, drop_key->key_len)) {
-            hmap_insert(&backer->drop_keys, &drop_key->hmap_node,
-                        hash_bytes(drop_key->key, drop_key->key_len, 0));
-            dpif_flow_put(backer->dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY,
-                          drop_key->key, drop_key->key_len,
-                          NULL, 0, NULL, 0, NULL);
-        } else {
-            drop_key_destroy(drop_key);
-        }
-    }
-
-    fmb = flow_miss_batch_next(backer->udpif);
-    if (fmb) {
-        handle_flow_misses(backer, fmb);
-        flow_miss_batch_destroy(fmb);
-    }
-}
-\f
-/* Flow expiration. */
-
-static int subfacet_max_idle(const struct dpif_backer *);
-static void update_stats(struct dpif_backer *);
-static void rule_expire(struct rule_dpif *) OVS_REQUIRES(ofproto_mutex);
-static void expire_subfacets(struct dpif_backer *, int dp_max_idle);
-
-/* This function is called periodically by run().  Its job is to collect
- * updates for the flows that have been installed into the datapath, most
- * importantly when they last were used, and then use that information to
- * expire flows that have not been used recently.
- *
- * Returns the number of milliseconds after which it should be called again. */
-static int
-expire(struct dpif_backer *backer)
-{
-    struct ofproto_dpif *ofproto;
-    size_t n_subfacets;
-    int max_idle;
-
-    /* Periodically clear out the drop keys in an effort to keep them
-     * relatively few. */
-    drop_key_clear(backer);
-
-    /* Update stats for each flow in the backer. */
-    update_stats(backer);
-
-    n_subfacets = hmap_count(&backer->subfacets);
-    if (n_subfacets) {
-        struct subfacet *subfacet;
-        long long int total, now;
-
-        total = 0;
-        now = time_msec();
-        HMAP_FOR_EACH (subfacet, hmap_node, &backer->subfacets) {
-            total += now - subfacet->created;
-        }
-        backer->avg_subfacet_life += total / n_subfacets;
-    }
-    backer->avg_subfacet_life /= 2;
-
-    backer->avg_n_subfacet += n_subfacets;
-    backer->avg_n_subfacet /= 2;
-
-    backer->max_n_subfacet = MAX(backer->max_n_subfacet, n_subfacets);
-
-    max_idle = subfacet_max_idle(backer);
-    expire_subfacets(backer, max_idle);
-
-    HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
-        struct rule *rule, *next_rule;
-
-        if (ofproto->backer != backer) {
-            continue;
-        }
-
-        /* Expire OpenFlow flows whose idle_timeout or hard_timeout
-         * has passed. */
-        ovs_mutex_lock(&ofproto_mutex);
-        LIST_FOR_EACH_SAFE (rule, next_rule, expirable,
-                            &ofproto->up.expirable) {
-            rule_expire(rule_dpif_cast(rule));
-        }
-        ovs_mutex_unlock(&ofproto_mutex);
-
-        /* All outstanding data in existing flows has been accounted, so it's a
-         * good time to do bond rebalancing. */
-        if (ofproto->has_bonded_bundles) {
-            struct ofbundle *bundle;
-
-            HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
-                if (bundle->bond) {
-                    bond_rebalance(bundle->bond);
-                }
-            }
-        }
-    }
-
-    return MIN(max_idle, 1000);
-}
-
-/* Updates flow table statistics given that the datapath just reported 'stats'
- * as 'subfacet''s statistics. */
-static void
-update_subfacet_stats(struct subfacet *subfacet,
-                      const struct dpif_flow_stats *stats)
-{
-    struct facet *facet = subfacet->facet;
-    struct dpif_flow_stats diff;
-
-    diff.tcp_flags = stats->tcp_flags;
-    diff.used = stats->used;
-
-    if (stats->n_packets >= subfacet->dp_packet_count) {
-        diff.n_packets = stats->n_packets - subfacet->dp_packet_count;
-    } else {
-        VLOG_WARN_RL(&rl, "unexpected packet count from the datapath");
-        diff.n_packets = 0;
-    }
-
-    if (stats->n_bytes >= subfacet->dp_byte_count) {
-        diff.n_bytes = stats->n_bytes - subfacet->dp_byte_count;
-    } else {
-        VLOG_WARN_RL(&rl, "unexpected byte count from datapath");
-        diff.n_bytes = 0;
-    }
-
-    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);
-
-    if (facet->accounted_bytes < facet->byte_count) {
-        facet_learn(facet);
-        facet_account(facet);
-        facet->accounted_bytes = facet->byte_count;
-    }
-}
-
-/* 'key' with length 'key_len' bytes is a flow in 'dpif' that we know nothing
- * about, or a flow that shouldn't be installed but was anyway.  Delete it. */
-static void
-delete_unexpected_flow(struct dpif_backer *backer,
-                       const struct nlattr *key, size_t key_len)
-{
-    if (!VLOG_DROP_WARN(&rl)) {
-        struct ds s;
-
-        ds_init(&s);
-        odp_flow_key_format(key, key_len, &s);
-        VLOG_WARN("unexpected flow: %s", ds_cstr(&s));
-        ds_destroy(&s);
-    }
-
-    COVERAGE_INC(facet_unexpected);
-    dpif_flow_del(backer->dpif, key, key_len, NULL);
-}
-
-/* Update 'packet_count', 'byte_count', and 'used' members of installed facets.
- *
- * This function also pushes statistics updates to rules which each facet
- * resubmits into.  Generally these statistics will be accurate.  However, if a
- * facet changes the rule it resubmits into at some time in between
- * update_stats() runs, it is possible that statistics accrued to the
- * old rule will be incorrectly attributed to the new rule.  This could be
- * avoided by calling update_stats() whenever rules are created or
- * deleted.  However, the performance impact of making so many calls to the
- * datapath do not justify the benefit of having perfectly accurate statistics.
- *
- * In addition, this function maintains per ofproto flow hit counts. The patch
- * port is not treated specially. e.g. A packet ingress from br0 patched into
- * br1 will increase the hit count of br0 by 1, however, does not affect
- * the hit or miss counts of br1.
- */
-static void
-update_stats(struct dpif_backer *backer)
-{
-    const struct dpif_flow_stats *stats;
-    struct dpif_flow_dump dump;
-    const struct nlattr *key, *mask;
-    size_t key_len, mask_len;
-
-    dpif_flow_dump_start(&dump, backer->dpif);
-    while (dpif_flow_dump_next(&dump, &key, &key_len,
-                               &mask, &mask_len, NULL, NULL, &stats)) {
-        struct subfacet *subfacet;
-        uint32_t key_hash;
-
-        key_hash = odp_flow_key_hash(key, key_len);
-        subfacet = subfacet_find(backer, key, key_len, key_hash);
-        switch (subfacet ? subfacet->path : SF_NOT_INSTALLED) {
-        case SF_FAST_PATH:
-            update_subfacet_stats(subfacet, stats);
-            break;
-
-        case SF_SLOW_PATH:
-            /* Stats are updated per-packet. */
-            break;
-
-        case SF_NOT_INSTALLED:
-        default:
-            delete_unexpected_flow(backer, key, key_len);
-            break;
-        }
-        run_fast_rl();
-    }
-    dpif_flow_dump_done(&dump);
-}
-
-/* Calculates and returns the number of milliseconds of idle time after which
- * subfacets should expire from the datapath.  When a subfacet expires, we fold
- * its statistics into its facet, and when a facet's last subfacet expires, we
- * fold its statistic into its rule. */
-static int
-subfacet_max_idle(const struct dpif_backer *backer)
-{
-    /*
-     * Idle time histogram.
-     *
-     * Most of the time a switch has a relatively small number of subfacets.
-     * When this is the case we might as well keep statistics for all of them
-     * in userspace and to cache them in the kernel datapath for performance as
-     * well.
-     *
-     * As the number of subfacets increases, the memory required to maintain
-     * statistics about them in userspace and in the kernel becomes
-     * significant.  However, with a large number of subfacets it is likely
-     * that only a few of them are "heavy hitters" that consume a large amount
-     * of bandwidth.  At this point, only heavy hitters are worth caching in
-     * the kernel and maintaining in userspaces; other subfacets we can
-     * discard.
-     *
-     * The technique used to compute the idle time is to build a histogram with
-     * N_BUCKETS buckets whose width is BUCKET_WIDTH msecs each.  Each subfacet
-     * that is installed in the kernel gets dropped in the appropriate bucket.
-     * After the histogram has been built, we compute the cutoff so that only
-     * the most-recently-used 1% of subfacets (but at least
-     * flow_eviction_threshold flows) are kept cached.  At least
-     * the most-recently-used bucket of subfacets is kept, so actually an
-     * arbitrary number of subfacets can be kept in any given expiration run
-     * (though the next run will delete most of those unless they receive
-     * additional data).
-     *
-     * This requires a second pass through the subfacets, in addition to the
-     * pass made by update_stats(), because the former function never looks at
-     * uninstallable subfacets.
-     */
-    enum { BUCKET_WIDTH = 100 };
-    enum { N_BUCKETS = 5000 / BUCKET_WIDTH };
-    int buckets[N_BUCKETS] = { 0 };
-    int total, subtotal, bucket;
-    struct subfacet *subfacet;
-    long long int now;
-    int i;
-
-    total = hmap_count(&backer->subfacets);
-    if (total <= flow_eviction_threshold) {
-        return N_BUCKETS * BUCKET_WIDTH;
-    }
-
-    /* Build histogram. */
-    now = time_msec();
-    HMAP_FOR_EACH (subfacet, hmap_node, &backer->subfacets) {
-        long long int idle = now - subfacet->used;
-        int bucket = (idle <= 0 ? 0
-                      : idle >= BUCKET_WIDTH * N_BUCKETS ? N_BUCKETS - 1
-                      : (unsigned int) idle / BUCKET_WIDTH);
-        buckets[bucket]++;
-    }
-
-    /* Find the first bucket whose flows should be expired. */
-    subtotal = bucket = 0;
-    do {
-        subtotal += buckets[bucket++];
-    } while (bucket < N_BUCKETS &&
-             subtotal < MAX(flow_eviction_threshold, total / 100));
-
-    if (VLOG_IS_DBG_ENABLED()) {
-        struct ds s;
-
-        ds_init(&s);
-        ds_put_cstr(&s, "keep");
-        for (i = 0; i < N_BUCKETS; i++) {
-            if (i == bucket) {
-                ds_put_cstr(&s, ", drop");
-            }
-            if (buckets[i]) {
-                ds_put_format(&s, " %d:%d", i * BUCKET_WIDTH, buckets[i]);
-            }
-        }
-        VLOG_INFO("%s (msec:count)", ds_cstr(&s));
-        ds_destroy(&s);
-    }
-
-    return bucket * BUCKET_WIDTH;
-}
-
-static void
-expire_subfacets(struct dpif_backer *backer, int dp_max_idle)
-{
-    /* Cutoff time for most flows. */
-    long long int normal_cutoff = time_msec() - dp_max_idle;
-
-    /* We really want to keep flows for special protocols around, so use a more
-     * conservative cutoff. */
-    long long int special_cutoff = time_msec() - 10000;
-
-    struct subfacet *subfacet, *next_subfacet;
-    struct subfacet *batch[SUBFACET_DESTROY_MAX_BATCH];
-    int n_batch;
-
-    n_batch = 0;
-    HMAP_FOR_EACH_SAFE (subfacet, next_subfacet, hmap_node,
-                        &backer->subfacets) {
-        long long int cutoff;
-
-        cutoff = (subfacet->facet->xout.slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP
-                                                | SLOW_STP)
-                  ? special_cutoff
-                  : normal_cutoff);
-        if (subfacet->used < cutoff) {
-            if (subfacet->path != SF_NOT_INSTALLED) {
-                batch[n_batch++] = subfacet;
-                if (n_batch >= SUBFACET_DESTROY_MAX_BATCH) {
-                    subfacet_destroy_batch(backer, batch, n_batch);
-                    n_batch = 0;
-                }
-            } else {
-                subfacet_destroy(subfacet);
-            }
-        }
-    }
-
-    if (n_batch > 0) {
-        subfacet_destroy_batch(backer, batch, n_batch);
-    }
-}
-
-/* If 'rule' is an OpenFlow rule, that has expired according to OpenFlow rules,
- * then delete it entirely. */
-static void
-rule_expire(struct rule_dpif *rule)
-    OVS_REQUIRES(ofproto_mutex)
-{
-    uint16_t idle_timeout, hard_timeout;
-    long long int now = time_msec();
-    int reason;
-
-    ovs_assert(!rule->up.pending);
-
-    /* Has 'rule' expired? */
-    ovs_mutex_lock(&rule->up.mutex);
-    hard_timeout = rule->up.hard_timeout;
-    idle_timeout = rule->up.idle_timeout;
-    if (hard_timeout && now > rule->up.modified + hard_timeout * 1000) {
-        reason = OFPRR_HARD_TIMEOUT;
-    } else if (idle_timeout && now > rule->up.used + idle_timeout * 1000) {
-        reason = OFPRR_IDLE_TIMEOUT;
-    } else {
-        reason = -1;
-    }
-    ovs_mutex_unlock(&rule->up.mutex);
+    ovs_mutex_unlock(&rule->up.mutex);
 
     if (reason >= 0) {
         COVERAGE_INC(ofproto_dpif_expired);
         ofproto_rule_expire(&rule->up, reason);
     }
 }
-\f
-/* Facets. */
-
-/* Creates and returns a new facet based on 'miss'.
- *
- * The caller must already have determined that no facet with an identical
- * 'miss->flow' exists in 'miss->ofproto'.
- *
- * 'rule' and 'xout' must have been created based on 'miss'.
- *
- * 'facet'' statistics are initialized based on 'stats'.
- *
- * The facet will initially have no subfacets.  The caller should create (at
- * least) one subfacet with subfacet_create(). */
-static struct facet *
-facet_create(const struct flow_miss *miss)
-{
-    struct ofproto_dpif *ofproto = miss->ofproto;
-    struct facet *facet;
-    struct match match;
-
-    COVERAGE_INC(facet_create);
-    facet = xzalloc(sizeof *facet);
-    facet->ofproto = miss->ofproto;
-    facet->used = miss->stats.used;
-    facet->flow = miss->flow;
-    facet->learn_rl = time_msec() + 500;
-
-    list_init(&facet->subfacets);
-    netflow_flow_init(&facet->nf_flow);
-    netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, facet->used);
-
-    xlate_out_copy(&facet->xout, &miss->xout);
-
-    match_init(&match, &facet->flow, &facet->xout.wc);
-    cls_rule_init(&facet->cr, &match, OFP_DEFAULT_PRIORITY);
-    ovs_rwlock_wrlock(&ofproto->facets.rwlock);
-    classifier_insert(&ofproto->facets, &facet->cr);
-    ovs_rwlock_unlock(&ofproto->facets.rwlock);
-
-    facet->nf_flow.output_iface = facet->xout.nf_output_iface;
-    return facet;
-}
-
-static void
-facet_free(struct facet *facet)
-{
-    if (facet) {
-        xlate_out_uninit(&facet->xout);
-        free(facet);
-    }
-}
 
 /* Executes, within 'ofproto', the actions in 'rule' or 'ofpacts' on 'packet'.
  * 'flow' must reflect the data in 'packet'. */
@@ -3856,494 +2886,44 @@ ofproto_dpif_execute_actions(struct ofproto_dpif *ofproto,
                              const struct ofpact *ofpacts, size_t ofpacts_len,
                              struct ofpbuf *packet)
 {
-    struct odputil_keybuf keybuf;
     struct dpif_flow_stats stats;
     struct xlate_out xout;
     struct xlate_in xin;
     ofp_port_t in_port;
-    struct ofpbuf key;
-    int error;
-
-    ovs_assert((rule != NULL) != (ofpacts != NULL));
-
-    dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
-    if (rule) {
-        rule_dpif_credit_stats(rule, &stats);
-    }
-
-    xlate_in_init(&xin, ofproto, flow, rule, stats.tcp_flags, packet);
-    xin.ofpacts = ofpacts;
-    xin.ofpacts_len = ofpacts_len;
-    xin.resubmit_stats = &stats;
-    xlate_actions(&xin, &xout);
-
-    ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
-    in_port = flow->in_port.ofp_port;
-    if (in_port == OFPP_NONE) {
-        in_port = OFPP_LOCAL;
-    }
-    odp_flow_key_from_flow(&key, flow, ofp_port_to_odp_port(ofproto, in_port));
-
-    error = dpif_execute(ofproto->backer->dpif, key.data, key.size,
-                         xout.odp_actions.data, xout.odp_actions.size, packet,
-                         (xout.slow & SLOW_ACTION) != 0);
-    xlate_out_uninit(&xout);
-
-    return error;
-}
-
-/* Remove 'facet' from its ofproto and free up the associated memory:
- *
- *   - If 'facet' was installed in the datapath, uninstalls it and updates its
- *     rule's statistics, via subfacet_uninstall().
- *
- *   - Removes 'facet' from its rule and from ofproto->facets.
- */
-static void
-facet_remove(struct facet *facet)
-{
-    struct subfacet *subfacet, *next_subfacet;
-
-    COVERAGE_INC(facet_remove);
-    ovs_assert(!list_is_empty(&facet->subfacets));
-
-    /* First uninstall all of the subfacets to get final statistics. */
-    LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
-        subfacet_uninstall(subfacet);
-    }
-
-    /* Flush the final stats to the rule.
-     *
-     * This might require us to have at least one subfacet around so that we
-     * can use its actions for accounting in facet_account(), which is why we
-     * have uninstalled but not yet destroyed the subfacets. */
-    facet_flush_stats(facet);
-
-    /* Now we're really all done so destroy everything. */
-    LIST_FOR_EACH_SAFE (subfacet, next_subfacet, list_node,
-                        &facet->subfacets) {
-        subfacet_destroy__(subfacet);
-    }
-    ovs_rwlock_wrlock(&facet->ofproto->facets.rwlock);
-    classifier_remove(&facet->ofproto->facets, &facet->cr);
-    ovs_rwlock_unlock(&facet->ofproto->facets.rwlock);
-    cls_rule_destroy(&facet->cr);
-    facet_free(facet);
-}
-
-/* Feed information from 'facet' back into the learning table to keep it in
- * sync with what is actually flowing through the datapath. */
-static void
-facet_learn(struct facet *facet)
-{
-    long long int now = time_msec();
-
-    if (!facet->xout.has_fin_timeout && now < facet->learn_rl) {
-        return;
-    }
-
-    facet->learn_rl = now + 500;
-
-    if (!facet->xout.has_learn
-        && !facet->xout.has_normal
-        && (!facet->xout.has_fin_timeout
-            || !(facet->tcp_flags & (TCP_FIN | TCP_RST)))) {
-        return;
-    }
-
-    facet_push_stats(facet, true);
-}
-
-static void
-facet_account(struct facet *facet)
-{
-    const struct nlattr *a;
-    unsigned int left;
-    ovs_be16 vlan_tci;
-    uint64_t n_bytes;
-
-    if (!facet->xout.has_normal || !facet->ofproto->has_bonded_bundles) {
-        return;
-    }
-    n_bytes = facet->byte_count - facet->accounted_bytes;
-
-    /* This loop feeds byte counters to bond_account() for rebalancing to use
-     * as a basis.  We also need to track the actual VLAN on which the packet
-     * is going to be sent to ensure that it matches the one passed to
-     * bond_choose_output_slave().  (Otherwise, we will account to the wrong
-     * hash bucket.)
-     *
-     * We use the actions from an arbitrary subfacet because they should all
-     * be equally valid for our purpose. */
-    vlan_tci = facet->flow.vlan_tci;
-    NL_ATTR_FOR_EACH_UNSAFE (a, left, facet->xout.odp_actions.data,
-                             facet->xout.odp_actions.size) {
-        const struct ovs_action_push_vlan *vlan;
-        struct ofport_dpif *port;
-
-        switch (nl_attr_type(a)) {
-        case OVS_ACTION_ATTR_OUTPUT:
-            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);
-            }
-            break;
-
-        case OVS_ACTION_ATTR_POP_VLAN:
-            vlan_tci = htons(0);
-            break;
-
-        case OVS_ACTION_ATTR_PUSH_VLAN:
-            vlan = nl_attr_get(a);
-            vlan_tci = vlan->vlan_tci;
-            break;
-        }
-    }
-}
-
-/* Returns true if the only action for 'facet' is to send to the controller.
- * (We don't report NetFlow expiration messages for such facets because they
- * are just part of the control logic for the network, not real traffic). */
-static bool
-facet_is_controller_flow(struct facet *facet)
-{
-    if (facet) {
-        struct ofproto_dpif *ofproto = facet->ofproto;
-        const struct ofpact *ofpacts;
-        struct rule_actions *actions;
-        struct rule_dpif *rule;
-        size_t ofpacts_len;
-        bool is_controller;
-
-        rule_dpif_lookup(ofproto, &facet->flow, NULL, &rule);
-        actions = rule_dpif_get_actions(rule);
-        rule_dpif_unref(rule);
-
-        ofpacts_len = actions->ofpacts_len;
-        ofpacts = actions->ofpacts;
-        is_controller = ofpacts_len > 0
-            && ofpacts->type == OFPACT_CONTROLLER
-            && ofpact_next(ofpacts) >= ofpact_end(ofpacts, ofpacts_len);
-        rule_actions_unref(actions);
-
-        return is_controller;
-    }
-    return false;
-}
-
-/* Folds all of 'facet''s statistics into its rule.  Also updates the
- * accounting ofhook and emits a NetFlow expiration if appropriate.  All of
- * 'facet''s statistics in the datapath should have been zeroed and folded into
- * its packet and byte counts before this function is called. */
-static void
-facet_flush_stats(struct facet *facet)
-{
-    struct ofproto_dpif *ofproto = facet->ofproto;
-    struct subfacet *subfacet;
-
-    LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
-        ovs_assert(!subfacet->dp_byte_count);
-        ovs_assert(!subfacet->dp_packet_count);
-    }
-
-    facet_push_stats(facet, false);
-    if (facet->accounted_bytes < facet->byte_count) {
-        facet_account(facet);
-        facet->accounted_bytes = facet->byte_count;
-    }
-
-    if (ofproto->netflow && !facet_is_controller_flow(facet)) {
-        struct ofexpired expired;
-        expired.flow = facet->flow;
-        expired.packet_count = facet->packet_count;
-        expired.byte_count = facet->byte_count;
-        expired.used = facet->used;
-        netflow_expire(ofproto->netflow, &facet->nf_flow, &expired);
-    }
-
-    /* Reset counters to prevent double counting if 'facet' ever gets
-     * reinstalled. */
-    facet_reset_counters(facet);
-
-    netflow_flow_clear(&facet->nf_flow);
-    facet->tcp_flags = 0;
-}
-
-/* Searches 'ofproto''s table of facets for one which would be responsible for
- * 'flow'.  Returns it if found, otherwise a null pointer.
- *
- * The returned facet might need revalidation; use facet_lookup_valid()
- * instead if that is important. */
-static struct facet *
-facet_find(struct ofproto_dpif *ofproto, const struct flow *flow)
-{
-    struct cls_rule *cr;
-
-    ovs_rwlock_rdlock(&ofproto->facets.rwlock);
-    cr = classifier_lookup(&ofproto->facets, flow, NULL);
-    ovs_rwlock_unlock(&ofproto->facets.rwlock);
-    return cr ? CONTAINER_OF(cr, struct facet, cr) : NULL;
-}
-
-/* Searches 'ofproto''s table of facets for one capable that covers
- * 'flow'.  Returns it if found, otherwise a null pointer.
- *
- * The returned facet is guaranteed to be valid. */
-static struct facet *
-facet_lookup_valid(struct ofproto_dpif *ofproto, const struct flow *flow)
-{
-    struct facet *facet;
-
-    facet = facet_find(ofproto, flow);
-    if (facet
-        && ofproto->backer->need_revalidate
-        && !facet_revalidate(facet)) {
-        return NULL;
-    }
-
-    return facet;
-}
-
-static bool
-facet_check_consistency(struct facet *facet)
-{
-    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 15);
-
-    struct xlate_out xout;
-    struct xlate_in xin;
-    bool ok;
-
-    /* Check the datapath actions for consistency. */
-    xlate_in_init(&xin, facet->ofproto, &facet->flow, NULL, 0, NULL);
-    xlate_actions(&xin, &xout);
-
-    ok = ofpbuf_equal(&facet->xout.odp_actions, &xout.odp_actions)
-        && facet->xout.slow == xout.slow;
-    if (!ok && !VLOG_DROP_WARN(&rl)) {
-        struct ds s = DS_EMPTY_INITIALIZER;
-
-        flow_format(&s, &facet->flow);
-        ds_put_cstr(&s, ": inconsistency in facet");
-
-        if (!ofpbuf_equal(&facet->xout.odp_actions, &xout.odp_actions)) {
-            ds_put_cstr(&s, " (actions were: ");
-            format_odp_actions(&s, facet->xout.odp_actions.data,
-                               facet->xout.odp_actions.size);
-            ds_put_cstr(&s, ") (correct actions: ");
-            format_odp_actions(&s, xout.odp_actions.data,
-                               xout.odp_actions.size);
-            ds_put_char(&s, ')');
-        }
-
-        if (facet->xout.slow != xout.slow) {
-            ds_put_format(&s, " slow path incorrect. should be %d", xout.slow);
-        }
-
-        ds_destroy(&s);
-    }
-    xlate_out_uninit(&xout);
-
-    return ok;
-}
-
-/* Re-searches the classifier for 'facet':
- *
- *   - If the rule found is different from 'facet''s current rule, moves
- *     'facet' to the new rule and recompiles its actions.
- *
- *   - If the rule found is the same as 'facet''s current rule, leaves 'facet'
- *     where it is and recompiles its actions anyway.
- *
- *   - If any of 'facet''s subfacets correspond to a new flow according to
- *     xlate_receive(), 'facet' is removed.
- *
- *   Returns true if 'facet' is still valid.  False if 'facet' was removed. */
-static bool
-facet_revalidate(struct facet *facet)
-{
-    struct ofproto_dpif *ofproto = facet->ofproto;
-    struct rule_dpif *new_rule;
-    struct subfacet *subfacet;
-    struct flow_wildcards wc;
-    struct xlate_out xout;
-    struct xlate_in xin;
-
-    COVERAGE_INC(facet_revalidate);
-
-    /* Check that child subfacets still correspond to this facet.  Tunnel
-     * configuration changes could cause a subfacet's OpenFlow in_port to
-     * change. */
-    LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
-        struct ofproto_dpif *recv_ofproto;
-        struct flow recv_flow;
-        int error;
-
-        error = xlate_receive(ofproto->backer, NULL, subfacet->key,
-                              subfacet->key_len, &recv_flow, NULL,
-                              &recv_ofproto, NULL);
-        if (error
-            || recv_ofproto != ofproto
-            || facet != facet_find(ofproto, &recv_flow)) {
-            facet_remove(facet);
-            return false;
-        }
-    }
-
-    flow_wildcards_init_catchall(&wc);
-    rule_dpif_lookup(ofproto, &facet->flow, &wc, &new_rule);
-
-    /* Calculate new datapath actions.
-     *
-     * We do not modify any 'facet' state yet, because we might need to, e.g.,
-     * emit a NetFlow expiration and, if so, we need to have the old state
-     * around to properly compose it. */
-    xlate_in_init(&xin, ofproto, &facet->flow, new_rule, 0, NULL);
-    xlate_actions(&xin, &xout);
-    flow_wildcards_or(&xout.wc, &xout.wc, &wc);
-
-    /* A facet's slow path reason should only change under dramatic
-     * circumstances.  Rather than try to update everything, it's simpler to
-     * remove the facet and start over.
-     *
-     * More importantly, if a facet's wildcards change, it will be relatively
-     * difficult to figure out if its subfacets still belong to it, and if not
-     * which facet they may belong to.  Again, to avoid the complexity, we
-     * simply give up instead. */
-    if (facet->xout.slow != xout.slow
-        || memcmp(&facet->xout.wc, &xout.wc, sizeof xout.wc)) {
-        facet_remove(facet);
-        xlate_out_uninit(&xout);
-        rule_dpif_unref(new_rule);
-        return false;
-    }
-
-    if (!ofpbuf_equal(&facet->xout.odp_actions, &xout.odp_actions)) {
-        LIST_FOR_EACH(subfacet, list_node, &facet->subfacets) {
-            if (subfacet->path == SF_FAST_PATH) {
-                struct dpif_flow_stats stats;
-
-                subfacet_install(subfacet, &xout.odp_actions, &stats);
-                subfacet_update_stats(subfacet, &stats);
-            }
-        }
-
-        facet_flush_stats(facet);
-
-        ofpbuf_clear(&facet->xout.odp_actions);
-        ofpbuf_put(&facet->xout.odp_actions, xout.odp_actions.data,
-                   xout.odp_actions.size);
-    }
-
-    /* Update 'facet' now that we've taken care of all the old state. */
-    facet->xout.slow = xout.slow;
-    facet->xout.has_learn = xout.has_learn;
-    facet->xout.has_normal = xout.has_normal;
-    facet->xout.has_fin_timeout = xout.has_fin_timeout;
-    facet->xout.nf_output_iface = xout.nf_output_iface;
-    facet->xout.mirrors = xout.mirrors;
-    facet->nf_flow.output_iface = facet->xout.nf_output_iface;
-
-    ovs_mutex_lock(&new_rule->up.mutex);
-    facet->used = MAX(facet->used, new_rule->up.created);
-    ovs_mutex_unlock(&new_rule->up.mutex);
-
-    xlate_out_uninit(&xout);
-    rule_dpif_unref(new_rule);
-    return true;
-}
-
-static void
-facet_reset_counters(struct facet *facet)
-{
-    facet->packet_count = 0;
-    facet->byte_count = 0;
-    facet->prev_packet_count = 0;
-    facet->prev_byte_count = 0;
-    facet->accounted_bytes = 0;
-}
-
-static void
-flow_push_stats(struct ofproto_dpif *ofproto, struct flow *flow,
-                struct dpif_flow_stats *stats, bool may_learn)
-{
-    struct ofport_dpif *in_port;
-    struct xlate_in xin;
-
-    in_port = get_ofp_port(ofproto, flow->in_port.ofp_port);
-    if (in_port && in_port->is_tunnel) {
-        netdev_vport_inc_rx(in_port->up.netdev, stats);
-        if (in_port->bfd) {
-            bfd_account_rx(in_port->bfd, stats);
-        }
-    }
-
-    xlate_in_init(&xin, ofproto, flow, NULL, stats->tcp_flags, NULL);
-    xin.resubmit_stats = stats;
-    xin.may_learn = may_learn;
-    xlate_actions_for_side_effects(&xin);
-}
-
-static void
-facet_push_stats(struct facet *facet, bool may_learn)
-{
-    struct dpif_flow_stats stats;
-
-    ovs_assert(facet->packet_count >= facet->prev_packet_count);
-    ovs_assert(facet->byte_count >= facet->prev_byte_count);
-    ovs_assert(facet->used >= facet->prev_used);
-
-    stats.n_packets = facet->packet_count - facet->prev_packet_count;
-    stats.n_bytes = facet->byte_count - facet->prev_byte_count;
-    stats.used = facet->used;
-    stats.tcp_flags = facet->tcp_flags;
+    struct dpif_execute execute;
+    int error;
 
-    if (may_learn || stats.n_packets || facet->used > facet->prev_used) {
-        facet->prev_packet_count = facet->packet_count;
-        facet->prev_byte_count = facet->byte_count;
-        facet->prev_used = facet->used;
+    ovs_assert((rule != NULL) != (ofpacts != NULL));
 
-        netflow_flow_update_time(facet->ofproto->netflow, &facet->nf_flow,
-                                 facet->used);
-        netflow_flow_update_flags(&facet->nf_flow, facet->tcp_flags);
-        mirror_update_stats(facet->ofproto->mbridge, facet->xout.mirrors,
-                            stats.n_packets, stats.n_bytes);
-        flow_push_stats(facet->ofproto, &facet->flow, &stats, may_learn);
+    dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
+    if (rule) {
+        rule_dpif_credit_stats(rule, &stats);
     }
-}
 
-static void
-push_all_stats__(bool run_fast)
-{
-    static long long int rl = LLONG_MIN;
-    struct ofproto_dpif *ofproto;
+    xlate_in_init(&xin, ofproto, flow, rule, stats.tcp_flags, packet);
+    xin.ofpacts = ofpacts;
+    xin.ofpacts_len = ofpacts_len;
+    xin.resubmit_stats = &stats;
+    xlate_actions(&xin, &xout);
 
-    if (time_msec() < rl) {
-        return;
+    in_port = flow->in_port.ofp_port;
+    if (in_port == OFPP_NONE) {
+        in_port = OFPP_LOCAL;
     }
+    execute.actions = xout.odp_actions.data;
+    execute.actions_len = xout.odp_actions.size;
+    execute.packet = packet;
+    execute.md.tunnel = flow->tunnel;
+    execute.md.skb_priority = flow->skb_priority;
+    execute.md.pkt_mark = flow->pkt_mark;
+    execute.md.in_port = ofp_port_to_odp_port(ofproto, in_port);
+    execute.needs_help = (xout.slow & SLOW_ACTION) != 0;
 
-    HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
-        struct cls_cursor cursor;
-        struct facet *facet;
-
-        ovs_rwlock_rdlock(&ofproto->facets.rwlock);
-        cls_cursor_init(&cursor, &ofproto->facets, NULL);
-        CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
-            facet_push_stats(facet, false);
-            if (run_fast) {
-                run_fast_rl();
-            }
-        }
-        ovs_rwlock_unlock(&ofproto->facets.rwlock);
-    }
+    error = dpif_execute(ofproto->backer->dpif, &execute);
 
-    rl = time_msec() + 100;
-}
+    xlate_out_uninit(&xout);
 
-static void
-push_all_stats(void)
-{
-    push_all_stats__(true);
+    return error;
 }
 
 void
@@ -4391,234 +2971,6 @@ rule_dpif_get_actions(const struct rule_dpif *rule)
 {
     return rule_get_actions(&rule->up);
 }
-\f
-/* Subfacets. */
-
-static struct subfacet *
-subfacet_find(struct dpif_backer *backer, const struct nlattr *key,
-              size_t key_len, uint32_t key_hash)
-{
-    struct subfacet *subfacet;
-
-    HMAP_FOR_EACH_WITH_HASH (subfacet, hmap_node, key_hash,
-                             &backer->subfacets) {
-        if (subfacet->key_len == key_len
-            && !memcmp(key, subfacet->key, key_len)) {
-            return subfacet;
-        }
-    }
-
-    return NULL;
-}
-
-/* Creates and returns a new subfacet within 'facet' for the flow in 'miss'.
- * 'key_hash' must be a hash over miss->key.  The caller must have already
- * ensured that no subfacet subfacet already exists. */
-static struct subfacet *
-subfacet_create(struct facet *facet, struct flow_miss *miss, uint32_t key_hash)
-{
-    struct dpif_backer *backer = miss->ofproto->backer;
-    const struct nlattr *key = miss->key;
-    size_t key_len = miss->key_len;
-    struct subfacet *subfacet;
-
-    subfacet = (list_is_empty(&facet->subfacets)
-                ? &facet->one_subfacet
-                : xmalloc(sizeof *subfacet));
-
-    COVERAGE_INC(subfacet_create);
-    hmap_insert(&backer->subfacets, &subfacet->hmap_node, key_hash);
-    list_push_back(&facet->subfacets, &subfacet->list_node);
-    subfacet->facet = facet;
-    subfacet->key = xmemdup(key, key_len);
-    subfacet->key_len = key_len;
-    subfacet->used = miss->stats.used;
-    subfacet->created = subfacet->used;
-    subfacet->dp_packet_count = 0;
-    subfacet->dp_byte_count = 0;
-    subfacet->path = SF_NOT_INSTALLED;
-    subfacet->backer = backer;
-
-    return subfacet;
-}
-
-/* Uninstalls 'subfacet' from the datapath, if it is installed, removes it from
- * its facet within 'ofproto', and frees it. */
-static void
-subfacet_destroy__(struct subfacet *subfacet)
-{
-    struct facet *facet = subfacet->facet;
-
-    COVERAGE_INC(subfacet_destroy);
-    subfacet_uninstall(subfacet);
-    hmap_remove(&subfacet->backer->subfacets, &subfacet->hmap_node);
-    list_remove(&subfacet->list_node);
-    free(subfacet->key);
-    if (subfacet != &facet->one_subfacet) {
-        free(subfacet);
-    }
-}
-
-/* Destroys 'subfacet', as with subfacet_destroy__(), and then if this was the
- * last remaining subfacet in its facet destroys the facet too. */
-static void
-subfacet_destroy(struct subfacet *subfacet)
-{
-    struct facet *facet = subfacet->facet;
-
-    if (list_is_singleton(&facet->subfacets)) {
-        /* facet_remove() needs at least one subfacet (it will remove it). */
-        facet_remove(facet);
-    } else {
-        subfacet_destroy__(subfacet);
-    }
-}
-
-static void
-subfacet_destroy_batch(struct dpif_backer *backer,
-                       struct subfacet **subfacets, int n)
-{
-    struct dpif_op ops[SUBFACET_DESTROY_MAX_BATCH];
-    struct dpif_op *opsp[SUBFACET_DESTROY_MAX_BATCH];
-    struct dpif_flow_stats stats[SUBFACET_DESTROY_MAX_BATCH];
-    int i;
-
-    for (i = 0; i < n; i++) {
-        ops[i].type = DPIF_OP_FLOW_DEL;
-        ops[i].u.flow_del.key = subfacets[i]->key;
-        ops[i].u.flow_del.key_len = subfacets[i]->key_len;
-        ops[i].u.flow_del.stats = &stats[i];
-        opsp[i] = &ops[i];
-    }
-
-    dpif_operate(backer->dpif, opsp, n);
-    for (i = 0; i < n; i++) {
-        subfacet_reset_dp_stats(subfacets[i], &stats[i]);
-        subfacets[i]->path = SF_NOT_INSTALLED;
-        subfacet_destroy(subfacets[i]);
-        run_fast_rl();
-    }
-}
-
-/* Updates 'subfacet''s datapath flow, setting its actions to 'actions_len'
- * bytes of actions in 'actions'.  If 'stats' is non-null, statistics counters
- * in the datapath will be zeroed and 'stats' will be updated with traffic new
- * since 'subfacet' was last updated.
- *
- * Returns 0 if successful, otherwise a positive errno value. */
-static int
-subfacet_install(struct subfacet *subfacet, const struct ofpbuf *odp_actions,
-                 struct dpif_flow_stats *stats)
-{
-    struct facet *facet = subfacet->facet;
-    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;
-    struct odputil_keybuf maskbuf;
-    struct ofpbuf mask;
-
-    uint64_t slow_path_stub[128 / 8];
-    enum dpif_flow_put_flags flags;
-    int ret;
-
-    flags = subfacet->path == SF_NOT_INSTALLED ? DPIF_FP_CREATE
-                                               : DPIF_FP_MODIFY;
-    if (stats) {
-        flags |= DPIF_FP_ZERO_STATS;
-    }
-
-    if (path == SF_SLOW_PATH) {
-        compose_slow_path(facet->ofproto, &facet->flow, facet->xout.slow,
-                          slow_path_stub, sizeof slow_path_stub,
-                          &actions, &actions_len);
-    }
-
-    ofpbuf_use_stack(&mask, &maskbuf, sizeof maskbuf);
-    if (enable_megaflows) {
-        odp_flow_key_from_mask(&mask, &facet->xout.wc.masks,
-                               &facet->flow, UINT32_MAX);
-    }
-
-    ret = dpif_flow_put(subfacet->backer->dpif, flags, subfacet->key,
-                        subfacet->key_len,  mask.data, mask.size,
-                        actions, actions_len, stats);
-
-    if (stats) {
-        subfacet_reset_dp_stats(subfacet, stats);
-    }
-
-    if (ret) {
-        COVERAGE_INC(subfacet_install_fail);
-    } else {
-        subfacet->path = path;
-    }
-    return ret;
-}
-
-/* If 'subfacet' is installed in the datapath, uninstalls it. */
-static void
-subfacet_uninstall(struct subfacet *subfacet)
-{
-    if (subfacet->path != SF_NOT_INSTALLED) {
-        struct ofproto_dpif *ofproto = subfacet->facet->ofproto;
-        struct dpif_flow_stats stats;
-        int error;
-
-        error = dpif_flow_del(ofproto->backer->dpif, subfacet->key,
-                              subfacet->key_len, &stats);
-        subfacet_reset_dp_stats(subfacet, &stats);
-        if (!error) {
-            subfacet_update_stats(subfacet, &stats);
-        }
-        subfacet->path = SF_NOT_INSTALLED;
-    } else {
-        ovs_assert(subfacet->dp_packet_count == 0);
-        ovs_assert(subfacet->dp_byte_count == 0);
-    }
-}
-
-/* Resets 'subfacet''s datapath statistics counters.  This should be called
- * when 'subfacet''s statistics are cleared in the datapath.  If 'stats' is
- * non-null, it should contain the statistics returned by dpif when 'subfacet'
- * was reset in the datapath.  'stats' will be modified to include only
- * statistics new since 'subfacet' was last updated. */
-static void
-subfacet_reset_dp_stats(struct subfacet *subfacet,
-                        struct dpif_flow_stats *stats)
-{
-    if (stats
-        && subfacet->dp_packet_count <= stats->n_packets
-        && subfacet->dp_byte_count <= stats->n_bytes) {
-        stats->n_packets -= subfacet->dp_packet_count;
-        stats->n_bytes -= subfacet->dp_byte_count;
-    }
-
-    subfacet->dp_packet_count = 0;
-    subfacet->dp_byte_count = 0;
-}
-
-/* Folds the statistics from 'stats' into the counters in 'subfacet'.
- *
- * Because of the meaning of a subfacet's counters, it only makes sense to do
- * this if 'stats' are not tracked in the datapath, that is, if 'stats'
- * represents a packet that was sent by hand or if it represents statistics
- * that have been cleared out of the datapath. */
-static void
-subfacet_update_stats(struct subfacet *subfacet,
-                      const struct dpif_flow_stats *stats)
-{
-    if (stats->n_packets || stats->used > subfacet->used) {
-        struct facet *facet = subfacet->facet;
-
-        subfacet->used = MAX(subfacet->used, stats->used);
-        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;
-    }
-}
-\f
-/* Rules. */
 
 /* Lookup 'flow' in 'ofproto''s classifier.  If 'wc' is non-null, sets
  * the fields that were relevant as part of the lookup. */
@@ -4779,14 +3131,6 @@ rule_get_stats(struct rule *rule_, uint64_t *packets, uint64_t *bytes)
 {
     struct rule_dpif *rule = rule_dpif_cast(rule_);
 
-    /* push_all_stats() can handle flow misses which, when using the learn
-     * action, can cause rules to be added and deleted.  This can corrupt our
-     * caller's datastructures which assume that rule_get_stats() doesn't have
-     * an impact on the flow table. To be safe, we disable miss handling. */
-    push_all_stats__(false);
-
-    /* Start from historical data for 'rule' itself that are no longer tracked
-     * in facets.  This counts, for example, facets that have expired. */
     ovs_mutex_lock(&rule->stats_mutex);
     *packets = rule->packet_count;
     *bytes = rule->byte_count;
@@ -4911,8 +3255,6 @@ group_get_stats(const struct ofgroup *group_, struct ofputil_group_stats *ogs)
 {
     struct group_dpif *group = group_dpif_cast(group_);
 
-    /* Start from historical data for 'group' itself that are no longer tracked
-     * in facets.  This counts, for example, facets that have expired. */
     ovs_mutex_lock(&group->stats_mutex);
     ogs->packet_count = group->packet_count;
     ogs->byte_count = group->byte_count;
@@ -4975,46 +3317,6 @@ ofproto_dpif_send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet
     ovs_mutex_unlock(&ofproto->stats_mutex);
     return error;
 }
-
-/* Composes an ODP action for a "slow path" action for 'flow' within 'ofproto'.
- * The action will state 'slow' as the reason that the action is in the slow
- * path.  (This is purely informational: it allows a human viewing "ovs-dpctl
- * dump-flows" output to see why a flow is in the slow path.)
- *
- * The 'stub_size' bytes in 'stub' will be used to store the action.
- * 'stub_size' must be large enough for the action.
- *
- * The action and its size will be stored in '*actionsp' and '*actions_lenp',
- * respectively. */
-static void
-compose_slow_path(const struct ofproto_dpif *ofproto, const struct flow *flow,
-                  enum slow_path_reason slow,
-                  uint64_t *stub, size_t stub_size,
-                  const struct nlattr **actionsp, size_t *actions_lenp)
-{
-    union user_action_cookie cookie;
-    struct ofpbuf buf;
-
-    cookie.type = USER_ACTION_COOKIE_SLOW_PATH;
-    cookie.slow_path.unused = 0;
-    cookie.slow_path.reason = slow;
-
-    ofpbuf_use_stack(&buf, stub, stub_size);
-    if (slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP | SLOW_STP)) {
-        uint32_t pid = dpif_port_get_pid(ofproto->backer->dpif,
-                                         ODPP_NONE);
-        odp_put_userspace_action(pid, &cookie, sizeof cookie.slow_path, &buf);
-    } else {
-        odp_port_t odp_port;
-        uint32_t pid;
-
-        odp_port = ofp_port_to_odp_port(ofproto, flow->in_port.ofp_port);
-        pid = dpif_port_get_pid(ofproto->backer->dpif, odp_port);
-        odp_put_userspace_action(pid, &cookie, sizeof cookie.slow_path, &buf);
-    }
-    *actionsp = buf.data;
-    *actions_lenp = buf.size;
-}
 \f
 static bool
 set_frag_handling(struct ofproto *ofproto_,
@@ -5057,7 +3359,7 @@ set_netflow(struct ofproto *ofproto_,
         return netflow_set_options(ofproto->netflow, netflow_options);
     } else if (ofproto->netflow) {
         ofproto->backer->need_revalidate = REV_RECONFIGURE;
-        netflow_destroy(ofproto->netflow);
+        netflow_unref(ofproto->netflow);
         ofproto->netflow = NULL;
     }
 
@@ -5072,46 +3374,6 @@ get_netflow_ids(const struct ofproto *ofproto_,
 
     dpif_get_netflow_ids(ofproto->backer->dpif, engine_type, engine_id);
 }
-
-static void
-send_active_timeout(struct ofproto_dpif *ofproto, struct facet *facet)
-{
-    if (!facet_is_controller_flow(facet) &&
-        netflow_active_timeout_expired(ofproto->netflow, &facet->nf_flow)) {
-        struct subfacet *subfacet;
-        struct ofexpired expired;
-
-        LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
-            if (subfacet->path == SF_FAST_PATH) {
-                struct dpif_flow_stats stats;
-
-                subfacet_install(subfacet, &facet->xout.odp_actions,
-                                 &stats);
-                subfacet_update_stats(subfacet, &stats);
-            }
-        }
-
-        expired.flow = facet->flow;
-        expired.packet_count = facet->packet_count;
-        expired.byte_count = facet->byte_count;
-        expired.used = facet->used;
-        netflow_expire(ofproto->netflow, &facet->nf_flow, &expired);
-    }
-}
-
-static void
-send_netflow_active_timeouts(struct ofproto_dpif *ofproto)
-{
-    struct cls_cursor cursor;
-    struct facet *facet;
-
-    ovs_rwlock_rdlock(&ofproto->facets.rwlock);
-    cls_cursor_init(&cursor, &ofproto->facets, NULL);
-    CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
-        send_active_timeout(ofproto, facet);
-    }
-    ovs_rwlock_unlock(&ofproto->facets.rwlock);
-}
 \f
 static struct ofproto_dpif *
 ofproto_dpif_lookup(const char *name)
@@ -5301,14 +3563,15 @@ trace_report(struct xlate_in *xin, const char *s, int recurse)
  *     - bridge br_flow [-generate | packet]
  *
  * On success, initializes '*ofprotop' and 'flow' and returns NULL.  On failure
- * returns a nonnull error message. */
-static const char *
+ * returns a nonnull malloced error message. */
+static char * WARN_UNUSED_RESULT
 parse_flow_and_packet(int argc, const char *argv[],
                       struct ofproto_dpif **ofprotop, struct flow *flow,
                       struct ofpbuf **packetp)
 {
     const struct dpif_backer *backer = NULL;
     const char *error = NULL;
+    char *m_err = NULL;
     struct simap port_names = SIMAP_INITIALIZER(&port_names);
     struct ofpbuf *packet;
     struct ofpbuf odp_key;
@@ -5329,6 +3592,7 @@ parse_flow_and_packet(int argc, const char *argv[],
             /* The 3-argument form must end in "-generate' or a hex string. */
             goto exit;
         }
+        error = NULL;
     }
 
     /* odp_flow can have its in_port specified as a name instead of port no.
@@ -5365,7 +3629,7 @@ parse_flow_and_packet(int argc, const char *argv[],
     /* Parse the flow and determine whether a datapath or
      * bridge is specified. If function odp_flow_key_from_string()
      * returns 0, the flow is a odp_flow. If function
-     * parse_ofp_exact_flow() returns 0, the flow is a br_flow. */
+     * parse_ofp_exact_flow() returns NULL, the flow is a br_flow. */
     if (!odp_flow_from_string(argv[argc - 1], &port_names,
                               &odp_key, &odp_mask)) {
         if (!backer) {
@@ -5374,24 +3638,29 @@ parse_flow_and_packet(int argc, const char *argv[],
         }
 
         if (xlate_receive(backer, NULL, odp_key.data, odp_key.size, flow,
-                          NULL, ofprotop, NULL)) {
+                          NULL, ofprotop, NULL, NULL, NULL, NULL)) {
             error = "Invalid datapath flow";
             goto exit;
         }
-    } else if (!parse_ofp_exact_flow(flow, NULL, argv[argc - 1], NULL)) {
-        if (argc != 3) {
-            error = "Must specify bridge name";
-            goto exit;
-        }
+    } else {
+        char *err = parse_ofp_exact_flow(flow, NULL, argv[argc - 1], NULL);
 
-        *ofprotop = ofproto_dpif_lookup(argv[1]);
-        if (!*ofprotop) {
-            error = "Unknown bridge name";
+        if (err) {
+            m_err = xasprintf("Bad flow syntax: %s", err);
+            free(err);
             goto exit;
+        } else {
+            if (argc != 3) {
+                error = "Must specify bridge name";
+                goto exit;
+            }
+
+            *ofprotop = ofproto_dpif_lookup(argv[1]);
+            if (!*ofprotop) {
+                error = "Unknown bridge name";
+                goto exit;
+            }
         }
-    } else {
-        error = "Bad flow syntax";
-        goto exit;
     }
 
     /* Generate a packet, if requested. */
@@ -5408,10 +3677,11 @@ parse_flow_and_packet(int argc, const char *argv[],
         }
     }
 
-    error = NULL;
-
 exit:
-    if (error) {
+    if (error && !m_err) {
+        m_err = xstrdup(error);
+    }
+    if (m_err) {
         ofpbuf_delete(packet);
         packet = NULL;
     }
@@ -5419,7 +3689,7 @@ exit:
     ofpbuf_uninit(&odp_key);
     ofpbuf_uninit(&odp_mask);
     simap_destroy(&port_names);
-    return error;
+    return m_err;
 }
 
 static void
@@ -5428,7 +3698,7 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
 {
     struct ofproto_dpif *ofproto;
     struct ofpbuf *packet;
-    const char *error;
+    char *error;
     struct flow flow;
 
     error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet);
@@ -5442,6 +3712,7 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
         ofpbuf_delete(packet);
     } else {
         unixctl_command_reply_error(conn, error);
+        free(error);
     }
 }
 
@@ -5460,18 +3731,17 @@ ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc,
 
     /* Three kinds of error return values! */
     enum ofperr retval;
-    const char *error;
-    char *rw_error;
+    char *error;
 
     packet = NULL;
     ds_init(&result);
     ofpbuf_init(&ofpacts, 0);
 
     /* Parse actions. */
-    rw_error = parse_ofpacts(argv[--argc], &ofpacts, &usable_protocols);
-    if (rw_error) {
-        unixctl_command_reply_error(conn, rw_error);
-        free(rw_error);
+    error = parse_ofpacts(argv[--argc], &ofpacts, &usable_protocols);
+    if (error) {
+        unixctl_command_reply_error(conn, error);
+        free(error);
         goto exit;
     }
 
@@ -5489,6 +3759,7 @@ ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc,
     error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet);
     if (error) {
         unixctl_command_reply_error(conn, error);
+        free(error);
         goto exit;
     }
 
@@ -5632,61 +3903,6 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
     rule_dpif_unref(rule);
 }
 
-/* Runs a self-check of flow translations in 'ofproto'.  Appends a message to
- * 'reply' describing the results. */
-static void
-ofproto_dpif_self_check__(struct ofproto_dpif *ofproto, struct ds *reply)
-{
-    struct cls_cursor cursor;
-    struct facet *facet;
-    int errors;
-
-    errors = 0;
-    ovs_rwlock_rdlock(&ofproto->facets.rwlock);
-    cls_cursor_init(&cursor, &ofproto->facets, NULL);
-    CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
-        if (!facet_check_consistency(facet)) {
-            errors++;
-        }
-    }
-    ovs_rwlock_unlock(&ofproto->facets.rwlock);
-    if (errors) {
-        ofproto->backer->need_revalidate = REV_INCONSISTENCY;
-    }
-
-    if (errors) {
-        ds_put_format(reply, "%s: self-check failed (%d errors)\n",
-                      ofproto->up.name, errors);
-    } else {
-        ds_put_format(reply, "%s: self-check passed\n", ofproto->up.name);
-    }
-}
-
-static void
-ofproto_dpif_self_check(struct unixctl_conn *conn,
-                        int argc, const char *argv[], void *aux OVS_UNUSED)
-{
-    struct ds reply = DS_EMPTY_INITIALIZER;
-    struct ofproto_dpif *ofproto;
-
-    if (argc > 1) {
-        ofproto = ofproto_dpif_lookup(argv[1]);
-        if (!ofproto) {
-            unixctl_command_reply_error(conn, "Unknown ofproto (use "
-                                        "ofproto/list for help)");
-            return;
-        }
-        ofproto_dpif_self_check__(ofproto, &reply);
-    } else {
-        HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
-            ofproto_dpif_self_check__(ofproto, &reply);
-        }
-    }
-
-    unixctl_command_reply(conn, ds_cstr(&reply));
-    ds_destroy(&reply);
-}
-
 /* Store the current ofprotos in 'ofproto_shash'.  Returns a sorted list
  * of the 'ofproto_shash' nodes.  It is the responsibility of the caller
  * to destroy 'ofproto_shash' and free the returned value. */
@@ -5731,25 +3947,14 @@ static void
 dpif_show_backer(const struct dpif_backer *backer, struct ds *ds)
 {
     const struct shash_node **ofprotos;
-    struct ofproto_dpif *ofproto;
+    struct dpif_dp_stats dp_stats;
     struct shash ofproto_shash;
-    uint64_t n_hit, n_missed;
     size_t i;
 
-    n_hit = n_missed = 0;
-    HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
-        if (ofproto->backer == backer) {
-            n_missed += ofproto->n_missed;
-            n_hit += ofproto->n_hit;
-        }
-    }
+    dpif_get_dp_stats(backer->dpif, &dp_stats);
 
     ds_put_format(ds, "%s: hit:%"PRIu64" missed:%"PRIu64"\n",
-                  dpif_name(backer->dpif), n_hit, n_missed);
-    ds_put_format(ds, "\tflows: cur: %"PRIuSIZE", avg: %u, max: %u,"
-                  " life span: %lldms\n", hmap_count(&backer->subfacets),
-                  backer->avg_n_subfacet, backer->max_n_subfacet,
-                  backer->avg_subfacet_life);
+                  dpif_name(backer->dpif), dp_stats.n_hit, dp_stats.n_missed);
 
     shash_init(&ofproto_shash);
     ofprotos = get_ofprotos(&ofproto_shash);
@@ -5762,8 +3967,7 @@ dpif_show_backer(const struct dpif_backer *backer, struct ds *ds)
             continue;
         }
 
-        ds_put_format(ds, "\t%s: hit:%"PRIu64" missed:%"PRIu64"\n",
-                      ofproto->up.name, ofproto->n_hit, ofproto->n_missed);
+        ds_put_format(ds, "\t%s:\n", ofproto->up.name);
 
         ports = shash_sort(&ofproto->up.port_by_name);
         for (j = 0; j < shash_count(&ofproto->up.port_by_name); j++) {
@@ -5826,102 +4030,17 @@ ofproto_unixctl_dpif_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
     ds_destroy(&ds);
 }
 
-/* Dump the megaflow (facet) cache.  This is useful to check the
- * correctness of flow wildcarding, since the same mechanism is used for
- * both xlate caching and kernel wildcarding.
- *
- * It's important to note that in the output the flow description uses
- * OpenFlow (OFP) ports, but the actions use datapath (ODP) ports.
- *
- * This command is only needed for advanced debugging, so it's not
- * documented in the man page. */
-static void
-ofproto_unixctl_dpif_dump_megaflows(struct unixctl_conn *conn,
-                                    int argc OVS_UNUSED, const char *argv[],
-                                    void *aux OVS_UNUSED)
-{
-    struct ds ds = DS_EMPTY_INITIALIZER;
-    const struct ofproto_dpif *ofproto;
-    long long int now = time_msec();
-    struct cls_cursor cursor;
-    struct facet *facet;
-
-    ofproto = ofproto_dpif_lookup(argv[1]);
-    if (!ofproto) {
-        unixctl_command_reply_error(conn, "no such bridge");
-        return;
-    }
-
-    ovs_rwlock_rdlock(&ofproto->facets.rwlock);
-    cls_cursor_init(&cursor, &ofproto->facets, NULL);
-    CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
-        cls_rule_format(&facet->cr, &ds);
-        ds_put_cstr(&ds, ", ");
-        ds_put_format(&ds, "n_subfacets:%"PRIuSIZE", ", list_size(&facet->subfacets));
-        ds_put_format(&ds, "used:%.3fs, ", (now - facet->used) / 1000.0);
-        ds_put_cstr(&ds, "Datapath actions: ");
-        if (facet->xout.slow) {
-            uint64_t slow_path_stub[128 / 8];
-            const struct nlattr *actions;
-            size_t actions_len;
-
-            compose_slow_path(ofproto, &facet->flow, facet->xout.slow,
-                              slow_path_stub, sizeof slow_path_stub,
-                              &actions, &actions_len);
-            format_odp_actions(&ds, actions, actions_len);
-        } else {
-            format_odp_actions(&ds, facet->xout.odp_actions.data,
-                               facet->xout.odp_actions.size);
-        }
-        ds_put_cstr(&ds, "\n");
-    }
-    ovs_rwlock_unlock(&ofproto->facets.rwlock);
-
-    ds_chomp(&ds, '\n');
-    unixctl_command_reply(conn, ds_cstr(&ds));
-    ds_destroy(&ds);
-}
-
-/* Disable using the megaflows.
- *
- * This command is only needed for advanced debugging, so it's not
- * documented in the man page. */
-static void
-ofproto_unixctl_dpif_disable_megaflows(struct unixctl_conn *conn,
-                                       int argc OVS_UNUSED,
-                                       const char *argv[] OVS_UNUSED,
-                                       void *aux OVS_UNUSED)
-{
-    struct ofproto_dpif *ofproto;
-
-    enable_megaflows = false;
-
-    HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
-        flush(&ofproto->up);
-    }
-
-    unixctl_command_reply(conn, "megaflows disabled");
-}
-
-/* Re-enable using megaflows.
- *
- * This command is only needed for advanced debugging, so it's not
- * documented in the man page. */
-static void
-ofproto_unixctl_dpif_enable_megaflows(struct unixctl_conn *conn,
-                                      int argc OVS_UNUSED,
-                                      const char *argv[] OVS_UNUSED,
-                                      void *aux OVS_UNUSED)
+static bool
+ofproto_dpif_contains_flow(const struct ofproto_dpif *ofproto,
+                           const struct nlattr *key, size_t key_len)
 {
-    struct ofproto_dpif *ofproto;
-
-    enable_megaflows = true;
-
-    HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
-        flush(&ofproto->up);
-    }
+    enum odp_key_fitness fitness;
+    struct ofproto_dpif *ofp;
+    struct flow flow;
 
-    unixctl_command_reply(conn, "megaflows enabled");
+    xlate_receive(ofproto->backer, NULL, key, key_len, &flow, &fitness, &ofp,
+                  NULL, NULL, NULL, NULL);
+    return ofp == ofproto;
 }
 
 static void
@@ -5930,86 +4049,61 @@ ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn,
                                 void *aux OVS_UNUSED)
 {
     struct ds ds = DS_EMPTY_INITIALIZER;
+    const struct dpif_flow_stats *stats;
     const struct ofproto_dpif *ofproto;
-    struct subfacet *subfacet;
+    struct dpif_flow_dump flow_dump;
+    const struct nlattr *actions;
+    const struct nlattr *mask;
+    const struct nlattr *key;
+    size_t actions_len;
+    size_t mask_len;
+    size_t key_len;
+    bool verbosity = false;
+    struct dpif_port dpif_port;
+    struct dpif_port_dump port_dump;
+    struct hmap portno_names;
 
-    ofproto = ofproto_dpif_lookup(argv[1]);
+    ofproto = ofproto_dpif_lookup(argv[argc - 1]);
     if (!ofproto) {
         unixctl_command_reply_error(conn, "no such bridge");
         return;
     }
 
-    update_stats(ofproto->backer);
+    if (argc > 2 && !strcmp(argv[1], "-m")) {
+        verbosity = true;
+    }
 
-    HMAP_FOR_EACH (subfacet, hmap_node, &ofproto->backer->subfacets) {
-        struct facet *facet = subfacet->facet;
-        struct odputil_keybuf maskbuf;
-        struct ofpbuf mask;
+    hmap_init(&portno_names);
+    DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, ofproto->backer->dpif) {
+        odp_portno_names_set(&portno_names, dpif_port.port_no, dpif_port.name);
+    }
 
-        if (facet->ofproto != ofproto) {
+    ds_init(&ds);
+    dpif_flow_dump_start(&flow_dump, ofproto->backer->dpif);
+    while (dpif_flow_dump_next(&flow_dump, &key, &key_len, &mask, &mask_len,
+                               &actions, &actions_len, &stats)) {
+        if (!ofproto_dpif_contains_flow(ofproto, key, key_len)) {
             continue;
         }
 
-        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, NULL, &ds, false);
-
-        ds_put_format(&ds, ", packets:%"PRIu64", bytes:%"PRIu64", used:",
-                      subfacet->dp_packet_count, subfacet->dp_byte_count);
-        if (subfacet->used) {
-            ds_put_format(&ds, "%.3fs",
-                          (time_msec() - subfacet->used) / 1000.0);
-        } else {
-            ds_put_format(&ds, "never");
-        }
-        if (subfacet->facet->tcp_flags) {
-            ds_put_cstr(&ds, ", flags:");
-            packet_format_tcp_flags(&ds, subfacet->facet->tcp_flags);
-        }
-
+        odp_flow_format(key, key_len, mask, mask_len, &portno_names, &ds,
+                        verbosity);
+        ds_put_cstr(&ds, ", ");
+        dpif_flow_stats_format(stats, &ds);
         ds_put_cstr(&ds, ", actions:");
-        if (facet->xout.slow) {
-            uint64_t slow_path_stub[128 / 8];
-            const struct nlattr *actions;
-            size_t actions_len;
-
-            compose_slow_path(ofproto, &facet->flow, facet->xout.slow,
-                              slow_path_stub, sizeof slow_path_stub,
-                              &actions, &actions_len);
-            format_odp_actions(&ds, actions, actions_len);
-        } else {
-            format_odp_actions(&ds, facet->xout.odp_actions.data,
-                               facet->xout.odp_actions.size);
-        }
+        format_odp_actions(&ds, actions, actions_len);
         ds_put_char(&ds, '\n');
     }
 
-    unixctl_command_reply(conn, ds_cstr(&ds));
-    ds_destroy(&ds);
-}
-
-static void
-ofproto_unixctl_dpif_del_flows(struct unixctl_conn *conn,
-                               int argc OVS_UNUSED, const char *argv[],
-                               void *aux OVS_UNUSED)
-{
-    struct ds ds = DS_EMPTY_INITIALIZER;
-    struct ofproto_dpif *ofproto;
-
-    ofproto = ofproto_dpif_lookup(argv[1]);
-    if (!ofproto) {
-        unixctl_command_reply_error(conn, "no such bridge");
-        return;
+    if (dpif_flow_dump_done(&flow_dump)) {
+        ds_clear(&ds);
+        ds_put_format(&ds, "dpif/dump_flows failed: %s", ovs_strerror(errno));
+        unixctl_command_reply_error(conn, ds_cstr(&ds));
+    } else {
+        unixctl_command_reply(conn, ds_cstr(&ds));
     }
-
-    flush(&ofproto->up);
-
-    unixctl_command_reply(conn, ds_cstr(&ds));
+    odp_portno_names_destroy(&portno_names);
+    hmap_destroy(&portno_names);
     ds_destroy(&ds);
 }
 
@@ -6034,22 +4128,12 @@ ofproto_dpif_unixctl_init(void)
                              ofproto_unixctl_fdb_flush, NULL);
     unixctl_command_register("fdb/show", "bridge", 1, 1,
                              ofproto_unixctl_fdb_show, NULL);
-    unixctl_command_register("ofproto/self-check", "[bridge]", 0, 1,
-                             ofproto_dpif_self_check, NULL);
     unixctl_command_register("dpif/dump-dps", "", 0, 0,
                              ofproto_unixctl_dpif_dump_dps, NULL);
     unixctl_command_register("dpif/show", "", 0, 0, ofproto_unixctl_dpif_show,
                              NULL);
-    unixctl_command_register("dpif/dump-flows", "bridge", 1, 1,
+    unixctl_command_register("dpif/dump-flows", "[-m] bridge", 1, 2,
                              ofproto_unixctl_dpif_dump_flows, NULL);
-    unixctl_command_register("dpif/del-flows", "bridge", 1, 1,
-                             ofproto_unixctl_dpif_del_flows, NULL);
-    unixctl_command_register("dpif/dump-megaflows", "bridge", 1, 1,
-                             ofproto_unixctl_dpif_dump_megaflows, NULL);
-    unixctl_command_register("dpif/disable-megaflows", "", 0, 0,
-                             ofproto_unixctl_dpif_disable_megaflows, NULL);
-    unixctl_command_register("dpif/enable-megaflows", "", 0, 0,
-                             ofproto_unixctl_dpif_enable_megaflows, NULL);
 }
 \f
 /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
@@ -6314,16 +4398,15 @@ const struct ofproto_class ofproto_dpif_class = {
     del,
     port_open_type,
     type_run,
-    type_run_fast,
     type_wait,
     alloc,
     construct,
     destruct,
     dealloc,
     run,
-    run_fast,
     wait,
-    get_memory_usage,
+    NULL,                       /* get_memory_usage. */
+    type_get_memory_usage,
     flush,
     get_features,
     get_tables,
@@ -6367,6 +4450,7 @@ const struct ofproto_class ofproto_dpif_class = {
     get_stp_status,
     set_stp_port,
     get_stp_port_status,
+    get_stp_port_stats,
     set_queues,
     bundle_set,
     bundle_remove,
index 2844e4c..cc318ee 100644 (file)
@@ -85,6 +85,7 @@ struct ofproto {
     uint16_t alloc_port_no;     /* Last allocated OpenFlow port number. */
     uint16_t max_ports;         /* Max possible OpenFlow port num, plus one. */
     struct hmap ofport_usage;   /* Map ofport to last used time. */
+    uint64_t change_seq;        /* Change sequence for netdev status. */
 
     /* Flow tables. */
     long long int eviction_group_timer; /* For rate limited reheapification. */
@@ -161,6 +162,8 @@ struct ofproto *ofproto_lookup(const char *name);
 struct ofport *ofproto_get_port(const struct ofproto *, ofp_port_t ofp_port);
 
 /* An OpenFlow port within a "struct ofproto".
+ *
+ * The port's name is netdev_get_name(port->netdev).
  *
  * With few exceptions, ofproto implementations may look at these fields but
  * should not modify them. */
@@ -170,7 +173,6 @@ struct ofport {
     struct netdev *netdev;
     struct ofputil_phy_port pp;
     ofp_port_t ofp_port;        /* OpenFlow port number. */
-    unsigned int change_seq;
     long long int created;      /* Time created, in msec. */
     int mtu;
 };
@@ -258,6 +260,9 @@ struct oftable {
     uint32_t eviction_group_id_basis;
     struct hmap eviction_groups_by_id;
     struct heap eviction_groups_by_size;
+
+    /* Table config: contains enum ofp_table_config; accessed atomically. */
+    atomic_uint config;
 };
 
 /* Assigns TABLE to each oftable, in turn, in OFPROTO.
@@ -450,13 +455,13 @@ void rule_collection_ref(struct rule_collection *) OVS_REQUIRES(ofproto_mutex);
 void rule_collection_unref(struct rule_collection *);
 void rule_collection_destroy(struct rule_collection *);
 
-/* Threshold at which to begin flow table eviction. Only affects the
- * ofproto-dpif implementation */
-extern unsigned flow_eviction_threshold;
+/* Limits the number of flows allowed in the datapath. Only affects the
+ * ofproto-dpif implementation. */
+extern unsigned ofproto_flow_limit;
 
-/* Number of upcall handler threads. Only affects the ofproto-dpif
- * implementation. */
-extern unsigned n_handler_threads;
+/* Number of upcall handler and revalidator threads. Only affects the
+ * ofproto-dpif implementation. */
+extern size_t n_handlers, n_revalidators;
 
 /* Determines which model to use for handling misses in the ofproto-dpif
  * implementation */
@@ -686,16 +691,6 @@ struct ofproto_class {
      * Returns 0 if successful, otherwise a positive errno value. */
     int (*type_run)(const char *type);
 
-    /* Performs periodic activity required on ofprotos of type 'type'
-     * that needs to be done with the least possible latency.
-     *
-     * This is run multiple times per main loop.  An ofproto provider may
-     * implement it or not, according to whether it provides a performance
-     * boost for that ofproto implementation.
-     *
-     * Returns 0 if successful, otherwise a positive errno value. */
-    int (*type_run_fast)(const char *type);
-
     /* Causes the poll loop to wake up when a type 'type''s 'run'
      * function needs to be called, e.g. by calling the timer or fd
      * waiting functions in poll-loop.h.
@@ -779,14 +774,6 @@ struct ofproto_class {
      * Returns 0 if successful, otherwise a positive errno value. */
     int (*run)(struct ofproto *ofproto);
 
-    /* Performs periodic activity required by 'ofproto' that needs to be done
-     * with the least possible latency.
-     *
-     * This is run multiple times per main loop.  An ofproto provider may
-     * implement it or not, according to whether it provides a performance
-     * boost for that ofproto implementation. */
-    int (*run_fast)(struct ofproto *ofproto);
-
     /* Causes the poll loop to wake up when 'ofproto''s 'run' function needs to
      * be called, e.g. by calling the timer or fd waiting functions in
      * poll-loop.h.  */
@@ -799,6 +786,12 @@ struct ofproto_class {
     void (*get_memory_usage)(const struct ofproto *ofproto,
                              struct simap *usage);
 
+    /* Adds some memory usage statistics for the implementation of 'type'
+     * into 'usage', for use with memory_report().
+     *
+     * This function is optional. */
+    void (*type_get_memory_usage)(const char *type, struct simap *usage);
+
     /* Every "struct rule" in 'ofproto' is about to be deleted, one by one.
      * This function may prepare for that, for example by clearing state in
      * advance.  It should *not* actually delete any "struct rule"s from
@@ -1530,6 +1523,16 @@ struct ofproto_class {
     int (*get_stp_port_status)(struct ofport *ofport,
                                struct ofproto_port_stp_status *s);
 
+    /* Retrieves spanning tree protocol (STP) port statistics of 'ofport'.
+     *
+     * Stores STP state for 'ofport' in 's'.  If the 'enabled' member is
+     * false, the other member values are not meaningful.
+     *
+     * EOPNOTSUPP as a return value indicates that this ofproto_class does not
+     * support STP, as does a null pointer. */
+    int (*get_stp_port_stats)(struct ofport *ofport,
+                              struct ofproto_port_stp_stats *s);
+
     /* Registers meta-data associated with the 'n_qdscp' Qualities of Service
      * 'queues' attached to 'ofport'.  This data is not intended to be
      * sufficient to implement QoS.  Instead, providers may use this
index b282abe..676a6cb 100644 (file)
@@ -25,6 +25,7 @@
 #include "bitmap.h"
 #include "byte-order.h"
 #include "classifier.h"
+#include "connectivity.h"
 #include "connmgr.h"
 #include "coverage.h"
 #include "dynamic-string.h"
@@ -47,6 +48,7 @@
 #include "pktbuf.h"
 #include "poll-loop.h"
 #include "random.h"
+#include "seq.h"
 #include "shash.h"
 #include "simap.h"
 #include "smap.h"
@@ -304,10 +306,11 @@ static size_t allocated_ofproto_classes;
 /* Global lock that protects all flow table operations. */
 struct ovs_mutex ofproto_mutex = OVS_MUTEX_INITIALIZER;
 
-unsigned flow_eviction_threshold = OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT;
-unsigned n_handler_threads;
+unsigned ofproto_flow_limit = OFPROTO_FLOW_LIMIT_DEFAULT;
 enum ofproto_flow_miss_model flow_miss_model = OFPROTO_HANDLE_MISS_AUTO;
 
+size_t n_handlers, n_revalidators;
+
 /* Map from datapath name to struct ofproto, for use by unixctl commands. */
 static struct hmap all_ofprotos = HMAP_INITIALIZER(&all_ofprotos);
 
@@ -432,6 +435,7 @@ ofproto_enumerate_types(struct sset *types)
 {
     size_t i;
 
+    sset_clear(types);
     for (i = 0; i < n_ofproto_classes; i++) {
         ofproto_classes[i]->enumerate_types(types);
     }
@@ -689,10 +693,9 @@ ofproto_set_in_band_queue(struct ofproto *ofproto, int queue_id)
 /* Sets the number of flows at which eviction from the kernel flow table
  * will occur. */
 void
-ofproto_set_flow_eviction_threshold(unsigned threshold)
+ofproto_set_flow_limit(unsigned limit)
 {
-    flow_eviction_threshold = MAX(OFPROTO_FLOW_EVICTION_THRESHOLD_MIN,
-                                  threshold);
+    ofproto_flow_limit = limit;
 }
 
 /* Sets the path for handling flow misses. */
@@ -730,16 +733,22 @@ ofproto_set_mac_table_config(struct ofproto *ofproto, unsigned idle_time,
     }
 }
 
-/* Sets number of upcall handler threads.  The default is
- * (number of online cores - 2). */
 void
-ofproto_set_n_handler_threads(unsigned limit)
+ofproto_set_threads(size_t n_handlers_, size_t n_revalidators_)
 {
-    if (limit) {
-        n_handler_threads = limit;
-    } else {
-        int n_proc = count_cpu_cores();
-        n_handler_threads = n_proc > 2 ? n_proc - 2 : 1;
+    int threads = MAX(count_cpu_cores(), 2);
+
+    n_revalidators = n_revalidators_;
+    n_handlers = n_handlers_;
+
+    if (!n_revalidators) {
+        n_revalidators = n_handlers
+            ? MAX(threads - (int) n_handlers, 1)
+            : threads / 4 + 1;
+    }
+
+    if (!n_handlers) {
+        n_handlers = MAX(threads - (int) n_revalidators, 1);
     }
 }
 
@@ -884,6 +893,27 @@ ofproto_port_get_stp_status(struct ofproto *ofproto, ofp_port_t ofp_port,
             ? ofproto->ofproto_class->get_stp_port_status(ofport, s)
             : EOPNOTSUPP);
 }
+
+/* Retrieves STP port statistics of 'ofp_port' on 'ofproto' and stores it in
+ * 's'.  If the 'enabled' member in 's' is false, then the other members
+ * are not meaningful.
+ *
+ * Returns 0 if successful, otherwise a positive errno value.*/
+int
+ofproto_port_get_stp_stats(struct ofproto *ofproto, ofp_port_t ofp_port,
+                           struct ofproto_port_stp_stats *s)
+{
+    struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
+    if (!ofport) {
+        VLOG_WARN_RL(&rl, "%s: cannot get STP stats on nonexistent "
+                     "port %"PRIu16, ofproto->name, ofp_port);
+        return ENODEV;
+    }
+
+    return (ofproto->ofproto_class->get_stp_port_stats
+            ? ofproto->ofproto_class->get_stp_port_stats(ofport, s)
+            : EOPNOTSUPP);
+}
 \f
 /* Queue DSCP configuration. */
 
@@ -1138,7 +1168,7 @@ ofproto_configure_table(struct ofproto *ofproto, int table_id,
     }
 
     table->max_flows = s->max_flows;
-    ovs_rwlock_rdlock(&table->cls.rwlock);
+    ovs_rwlock_wrlock(&table->cls.rwlock);
     if (classifier_count(&table->cls) > table->max_flows
         && table->eviction_fields) {
         /* 'table' contains more flows than allowed.  We might not be able to
@@ -1154,6 +1184,10 @@ ofproto_configure_table(struct ofproto *ofproto, int table_id,
             break;
         }
     }
+
+    classifier_set_prefix_fields(&table->cls,
+                                 s->prefix_fields, s->n_prefix_fields);
+
     ovs_rwlock_unlock(&table->cls.rwlock);
 }
 \f
@@ -1360,23 +1394,6 @@ ofproto_type_run(const char *datapath_type)
     return error;
 }
 
-int
-ofproto_type_run_fast(const char *datapath_type)
-{
-    const struct ofproto_class *class;
-    int error;
-
-    datapath_type = ofproto_normalize_type(datapath_type);
-    class = ofproto_class_find__(datapath_type);
-
-    error = class->type_run_fast ? class->type_run_fast(datapath_type) : 0;
-    if (error && error != EAGAIN) {
-        VLOG_ERR_RL(&rl, "%s: type_run_fast failed (%s)",
-                    datapath_type, ovs_strerror(error));
-    }
-    return error;
-}
-
 void
 ofproto_type_wait(const char *datapath_type)
 {
@@ -1406,10 +1423,8 @@ any_pending_ops(const struct ofproto *p)
 int
 ofproto_run(struct ofproto *p)
 {
-    struct sset changed_netdevs;
-    const char *changed_netdev;
-    struct ofport *ofport;
     int error;
+    uint64_t new_seq;
 
     error = p->ofproto_class->run(p);
     if (error && error != EAGAIN) {
@@ -1460,24 +1475,29 @@ ofproto_run(struct ofproto *p)
         }
     }
 
-    /* Update OpenFlow port status for any port whose netdev has changed.
-     *
-     * Refreshing a given 'ofport' can cause an arbitrary ofport to be
-     * destroyed, so it's not safe to update ports directly from the
-     * HMAP_FOR_EACH loop, or even to use HMAP_FOR_EACH_SAFE.  Instead, we
-     * need this two-phase approach. */
-    sset_init(&changed_netdevs);
-    HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
-        unsigned int change_seq = netdev_change_seq(ofport->netdev);
-        if (ofport->change_seq != change_seq) {
-            ofport->change_seq = change_seq;
-            sset_add(&changed_netdevs, netdev_get_name(ofport->netdev));
+    new_seq = seq_read(connectivity_seq_get());
+    if (new_seq != p->change_seq) {
+        struct sset devnames;
+        const char *devname;
+        struct ofport *ofport;
+
+        /* Update OpenFlow port status for any port whose netdev has changed.
+         *
+         * Refreshing a given 'ofport' can cause an arbitrary ofport to be
+         * destroyed, so it's not safe to update ports directly from the
+         * HMAP_FOR_EACH loop, or even to use HMAP_FOR_EACH_SAFE.  Instead, we
+         * need this two-phase approach. */
+        sset_init(&devnames);
+        HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
+            sset_add(&devnames, netdev_get_name(ofport->netdev));
         }
+        SSET_FOR_EACH (devname, &devnames) {
+            update_port(p, devname);
+        }
+        sset_destroy(&devnames);
+
+        p->change_seq = new_seq;
     }
-    SSET_FOR_EACH (changed_netdev, &changed_netdevs) {
-        update_port(p, changed_netdev);
-    }
-    sset_destroy(&changed_netdevs);
 
     switch (p->state) {
     case S_OPENFLOW:
@@ -1502,7 +1522,7 @@ ofproto_run(struct ofproto *p)
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     if (time_msec() >= p->next_op_report) {
@@ -1545,40 +1565,14 @@ ofproto_run(struct ofproto *p)
     return error;
 }
 
-/* Performs periodic activity required by 'ofproto' that needs to be done
- * with the least possible latency.
- *
- * It makes sense to call this function a couple of times per poll loop, to
- * provide a significant performance boost on some benchmarks with the
- * ofproto-dpif implementation. */
-int
-ofproto_run_fast(struct ofproto *p)
-{
-    int error;
-
-    error = p->ofproto_class->run_fast ? p->ofproto_class->run_fast(p) : 0;
-    if (error && error != EAGAIN) {
-        VLOG_ERR_RL(&rl, "%s: fastpath run failed (%s)",
-                    p->name, ovs_strerror(error));
-    }
-    return error;
-}
-
 void
 ofproto_wait(struct ofproto *p)
 {
-    struct ofport *ofport;
-
     p->ofproto_class->wait(p);
     if (p->ofproto_class->port_poll_wait) {
         p->ofproto_class->port_poll_wait(p);
     }
-
-    HMAP_FOR_EACH (ofport, hmap_node, &p->ports) {
-        if (ofport->change_seq != netdev_change_seq(ofport->netdev)) {
-            poll_immediate_wake();
-        }
-    }
+    seq_wait(connectivity_seq_get(), p->change_seq);
 
     switch (p->state) {
     case S_OPENFLOW:
@@ -1631,6 +1625,19 @@ ofproto_get_memory_usage(const struct ofproto *ofproto, struct simap *usage)
     connmgr_get_memory_usage(ofproto->connmgr, usage);
 }
 
+void
+ofproto_type_get_memory_usage(const char *datapath_type, struct simap *usage)
+{
+    const struct ofproto_class *class;
+
+    datapath_type = ofproto_normalize_type(datapath_type);
+    class = ofproto_class_find__(datapath_type);
+
+    if (class && class->type_get_memory_usage) {
+        class->type_get_memory_usage(datapath_type, usage);
+    }
+}
+
 void
 ofproto_get_ofproto_controller_info(const struct ofproto *ofproto,
                                     struct shash *info)
@@ -2138,7 +2145,6 @@ ofport_install(struct ofproto *p,
     }
     ofport->ofproto = p;
     ofport->netdev = netdev;
-    ofport->change_seq = netdev_change_seq(netdev);
     ofport->pp = *pp;
     ofport->ofp_port = pp->port_no;
     ofport->created = time_msec();
@@ -2373,7 +2379,6 @@ update_port(struct ofproto *ofproto, const char *name)
              * Don't close the old netdev yet in case port_modified has to
              * remove a retained reference to it.*/
             port->netdev = netdev;
-            port->change_seq = netdev_change_seq(netdev);
 
             if (port->ofproto->ofproto_class->port_modified) {
                 port->ofproto->ofproto_class->port_modified(port);
@@ -2670,7 +2675,7 @@ ofoperation_has_out_port(const struct ofoperation *op, ofp_port_t out_port)
                                       op->actions->ofpacts_len, out_port);
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 static void
@@ -3222,14 +3227,11 @@ calc_duration(long long int start, long long int now,
 }
 
 /* Checks whether 'table_id' is 0xff or a valid table ID in 'ofproto'.  Returns
- * 0 if 'table_id' is OK, otherwise an OpenFlow error code.  */
-static enum ofperr
+ * true if 'table_id' is OK, false otherwise.  */
+static bool
 check_table_id(const struct ofproto *ofproto, uint8_t table_id)
 {
-    return (table_id == 0xff || table_id < ofproto->n_tables
-            ? 0
-            : OFPERR_OFPBRC_BAD_TABLE_ID);
-
+    return table_id == OFPTT_ALL || table_id < ofproto->n_tables;
 }
 
 static struct oftable *
@@ -3413,12 +3415,12 @@ collect_rules_loose(struct ofproto *ofproto,
     OVS_REQUIRES(ofproto_mutex)
 {
     struct oftable *table;
-    enum ofperr error;
+    enum ofperr error = 0;
 
     rule_collection_init(rules);
 
-    error = check_table_id(ofproto, criteria->table_id);
-    if (error) {
+    if (!check_table_id(ofproto, criteria->table_id)) {
+        error = OFPERR_OFPBRC_BAD_TABLE_ID;
         goto exit;
     }
 
@@ -3474,12 +3476,12 @@ collect_rules_strict(struct ofproto *ofproto,
     OVS_REQUIRES(ofproto_mutex)
 {
     struct oftable *table;
-    int error;
+    int error = 0;
 
     rule_collection_init(rules);
 
-    error = check_table_id(ofproto, criteria->table_id);
-    if (error) {
+    if (!check_table_id(ofproto, criteria->table_id)) {
+        error = OFPERR_OFPBRC_BAD_TABLE_ID;
         goto exit;
     }
 
@@ -3932,10 +3934,10 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     struct cls_rule cr;
     struct rule *rule;
     uint8_t table_id;
-    int error;
+    int error = 0;
 
-    error = check_table_id(ofproto, fm->table_id);
-    if (error) {
+    if (!check_table_id(ofproto, fm->table_id)) {
+        error = OFPERR_OFPBRC_BAD_TABLE_ID;
         return error;
     }
 
@@ -4726,7 +4728,7 @@ ofproto_compose_flow_refresh_update(const struct rule *rule,
          * actions, so that when the operation commits we report the change. */
         switch (op->type) {
         case OFOPERATION_ADD:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
 
         case OFOPERATION_MODIFY:
         case OFOPERATION_REPLACE:
@@ -4738,7 +4740,7 @@ ofproto_compose_flow_refresh_update(const struct rule *rule,
             break;
 
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
     fu.ofpacts = actions ? actions->ofpacts : NULL;
@@ -5776,9 +5778,32 @@ handle_group_mod(struct ofconn *ofconn, const struct ofp_header *oh)
     }
 }
 
+static enum ofperr
+table_mod(struct ofproto *ofproto, const struct ofputil_table_mod *tm)
+{
+    /* XXX Reject all configurations because none are currently supported */
+    return OFPERR_OFPTMFC_BAD_CONFIG;
+
+    if (tm->table_id == OFPTT_ALL) {
+        int i;
+        for (i = 0; i < ofproto->n_tables; i++) {
+            atomic_store(&ofproto->tables[i].config,
+                         (unsigned int)tm->config);
+        }
+    } else if (!check_table_id(ofproto, tm->table_id)) {
+        return OFPERR_OFPTMFC_BAD_TABLE;
+    } else {
+        atomic_store(&ofproto->tables[tm->table_id].config,
+                     (unsigned int)tm->config);
+    }
+
+    return 0;
+}
+
 static enum ofperr
 handle_table_mod(struct ofconn *ofconn, const struct ofp_header *oh)
 {
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
     struct ofputil_table_mod tm;
     enum ofperr error;
 
@@ -5792,8 +5817,7 @@ handle_table_mod(struct ofconn *ofconn, const struct ofp_header *oh)
         return error;
     }
 
-    /* XXX Actual table mod support is not implemented yet. */
-    return 0;
+    return table_mod(ofproto, &tm);
 }
 
 static enum ofperr
@@ -6143,7 +6167,7 @@ ofopgroup_complete(struct ofopgroup *group)
                 break;
 
             default:
-                NOT_REACHED();
+                OVS_NOT_REACHED();
             }
 
             ofmonitor_report(ofproto->connmgr, rule, event_type,
@@ -6212,7 +6236,7 @@ ofopgroup_complete(struct ofopgroup *group)
             break;
 
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
 
         ofoperation_destroy(op);
@@ -6626,6 +6650,7 @@ oftable_init(struct oftable *table)
     memset(table, 0, sizeof *table);
     classifier_init(&table->cls, flow_segment_u32s);
     table->max_flows = UINT_MAX;
+    atomic_init(&table->config, (unsigned int)OFPTC11_TABLE_MISS_CONTROLLER);
 }
 
 /* Destroys 'table', including its classifier and eviction groups.
index 903d1f4..3034d32 100644 (file)
@@ -23,7 +23,9 @@
 #include <stddef.h>
 #include <stdint.h>
 #include "cfm.h"
+#include "classifier.h"
 #include "flow.h"
+#include "meta-flow.h"
 #include "netflow.h"
 #include "sset.h"
 #include "stp.h"
@@ -36,12 +38,12 @@ struct bfd_cfg;
 struct cfm_settings;
 struct cls_rule;
 struct netdev;
-struct ofproto;
+struct netdev_stats;
 struct ofport;
+struct ofproto;
 struct shash;
 struct simap;
 struct smap;
-struct netdev_stats;
 
 struct ofproto_controller_info {
     bool is_connected;
@@ -53,13 +55,6 @@ struct ofproto_controller_info {
     } pairs;
 };
 
-struct ofexpired {
-    struct flow flow;
-    uint64_t packet_count;      /* Packets from subrules. */
-    uint64_t byte_count;        /* Bytes from subrules. */
-    long long int used;         /* Last-used time (0 if never used). */
-};
-
 struct ofproto_sflow_options {
     struct sset targets;
     uint32_t sampling_rate;
@@ -70,7 +65,6 @@ struct ofproto_sflow_options {
     char *control_ip;
 };
 
-
 struct ofproto_ipfix_bridge_exporter_options {
     struct sset targets;
     uint32_t sampling_rate;
@@ -115,6 +109,10 @@ struct ofproto_port_stp_status {
     enum stp_state state;
     unsigned int sec_in_state;
     enum stp_role role;
+};
+
+struct ofproto_port_stp_stats {
+    bool enabled;               /* If false, ignore other members. */
     int tx_count;               /* Number of BPDUs transmitted. */
     int rx_count;               /* Number of valid BPDUs received. */
     int error_count;            /* Number of bad BPDUs received. */
@@ -167,7 +165,6 @@ struct iface_hint {
 void ofproto_init(const struct shash *iface_hints);
 
 int ofproto_type_run(const char *datapath_type);
-int ofproto_type_run_fast(const char *datapath_type);
 void ofproto_type_wait(const char *datapath_type);
 
 int ofproto_create(const char *datapath, const char *datapath_type,
@@ -176,11 +173,11 @@ void ofproto_destroy(struct ofproto *);
 int ofproto_delete(const char *name, const char *type);
 
 int ofproto_run(struct ofproto *);
-int ofproto_run_fast(struct ofproto *);
 void ofproto_wait(struct ofproto *);
 bool ofproto_is_alive(const struct ofproto *);
 
 void ofproto_get_memory_usage(const struct ofproto *, struct simap *);
+void ofproto_type_get_memory_usage(const char *datapath_type, struct simap *);
 
 /* A port within an OpenFlow switch.
  *
@@ -216,8 +213,7 @@ int ofproto_port_dump_done(struct ofproto_port_dump *);
           : (ofproto_port_dump_done(DUMP), false));         \
         )
 
-#define OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT  2500
-#define OFPROTO_FLOW_EVICTION_THRESHOLD_MIN 100
+#define OFPROTO_FLOW_LIMIT_DEFAULT 200000
 
 /* How flow misses should be handled in ofproto-dpif */
 enum ofproto_flow_miss_model {
@@ -246,12 +242,12 @@ void ofproto_reconnect_controllers(struct ofproto *);
 void ofproto_set_extra_in_band_remotes(struct ofproto *,
                                        const struct sockaddr_in *, size_t n);
 void ofproto_set_in_band_queue(struct ofproto *, int queue_id);
-void ofproto_set_flow_eviction_threshold(unsigned threshold);
+void ofproto_set_flow_limit(unsigned limit);
 void ofproto_set_flow_miss_model(unsigned model);
 void ofproto_set_forward_bpdu(struct ofproto *, bool forward_bpdu);
 void ofproto_set_mac_table_config(struct ofproto *, unsigned idle_time,
                                   size_t max_entries);
-void ofproto_set_n_handler_threads(unsigned limit);
+void ofproto_set_threads(size_t n_handlers, size_t n_revalidators);
 void ofproto_set_dp_desc(struct ofproto *, const char *dp_desc);
 int ofproto_set_snoops(struct ofproto *, const struct sset *snoops);
 int ofproto_set_netflow(struct ofproto *,
@@ -281,6 +277,8 @@ int ofproto_port_set_stp(struct ofproto *, ofp_port_t ofp_port,
                          const struct ofproto_port_stp_settings *);
 int ofproto_port_get_stp_status(struct ofproto *, ofp_port_t ofp_port,
                                 struct ofproto_port_stp_status *);
+int ofproto_port_get_stp_stats(struct ofproto *, ofp_port_t ofp_port,
+                               struct ofproto_port_stp_stats *);
 int ofproto_port_set_queues(struct ofproto *, ofp_port_t ofp_port,
                             const struct ofproto_port_queue *,
                             size_t n_queues);
@@ -381,6 +379,12 @@ struct ofproto_table_settings {
      * distinguished by different values for the subfields within 'groups'. */
     struct mf_subfield *groups;
     size_t n_groups;
+
+    /*
+     * Fields for which prefix trie lookup is maintained.
+     */
+    unsigned int n_prefix_fields;
+    enum mf_field_id prefix_fields[CLS_MAX_TRIES];
 };
 
 int ofproto_get_n_tables(const struct ofproto *);
index b238cd0..d55adde 100644 (file)
 #include <errno.h>
 
 #include "byte-order.h"
+#include "connectivity.h"
 #include "dynamic-string.h"
 #include "hash.h"
 #include "hmap.h"
 #include "netdev.h"
 #include "odp-util.h"
 #include "packets.h"
+#include "seq.h"
 #include "smap.h"
 #include "socket-util.h"
 #include "tunnel.h"
@@ -50,7 +52,7 @@ struct tnl_port {
     struct hmap_node match_node;
 
     const struct ofport_dpif *ofport;
-    unsigned int netdev_seq;
+    unsigned int change_seq;
     struct netdev *netdev;
 
     struct tnl_match match;
@@ -97,7 +99,7 @@ tnl_port_add__(const struct ofport_dpif *ofport, const struct netdev *netdev,
     tnl_port = xzalloc(sizeof *tnl_port);
     tnl_port->ofport = ofport;
     tnl_port->netdev = netdev_ref(netdev);
-    tnl_port->netdev_seq = netdev_change_seq(tnl_port->netdev);
+    tnl_port->change_seq = seq_read(connectivity_seq_get());
 
     tnl_port->match.in_key = cfg->in_key;
     tnl_port->match.ip_src = cfg->ip_src;
@@ -159,7 +161,7 @@ tnl_port_reconfigure(const struct ofport_dpif *ofport,
         changed = tnl_port_add__(ofport, netdev, odp_port, false);
     } else if (tnl_port->netdev != netdev
                || tnl_port->match.odp_port != odp_port
-               || tnl_port->netdev_seq != netdev_change_seq(netdev)) {
+               || tnl_port->change_seq != seq_read(connectivity_seq_get())) {
         VLOG_DBG("reconfiguring %s", tnl_port_get_name(tnl_port));
         tnl_port_del__(ofport);
         tnl_port_add__(ofport, netdev, odp_port, true);
index 3884d3f..b97365e 100644 (file)
@@ -1,6 +1,7 @@
 # libovsdb
-noinst_LIBRARIES += ovsdb/libovsdb.a
-ovsdb_libovsdb_a_SOURCES = \
+lib_LTLIBRARIES += ovsdb/libovsdb.la
+ovsdb_libovsdb_la_LDFLAGS = -release $(VERSION)
+ovsdb_libovsdb_la_SOURCES = \
        ovsdb/column.c \
        ovsdb/column.h \
        ovsdb/condition.c \
@@ -29,6 +30,9 @@ ovsdb_libovsdb_a_SOURCES = \
        ovsdb/trigger.h \
        ovsdb/transaction.c \
        ovsdb/transaction.h
+ovsdb_libovsdb_la_CFLAGS = $(AM_CFLAGS)
+ovsdb_libovsdb_la_CPPFLAGS = $(AM_CPPFLAGS)
+
 MAN_FRAGMENTS += \
        ovsdb/remote-active.man \
        ovsdb/remote-passive.man
@@ -36,7 +40,7 @@ MAN_FRAGMENTS += \
 # ovsdb-tool
 bin_PROGRAMS += ovsdb/ovsdb-tool
 ovsdb_ovsdb_tool_SOURCES = ovsdb/ovsdb-tool.c
-ovsdb_ovsdb_tool_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a $(SSL_LIBS)
+ovsdb_ovsdb_tool_LDADD = ovsdb/libovsdb.la lib/libopenvswitch.la $(SSL_LIBS)
 # ovsdb-tool.1
 man_MANS += ovsdb/ovsdb-tool.1
 DISTCLEANFILES += ovsdb/ovsdb-tool.1
@@ -45,7 +49,7 @@ MAN_ROOTS += ovsdb/ovsdb-tool.1.in
 # ovsdb-client
 bin_PROGRAMS += ovsdb/ovsdb-client
 ovsdb_ovsdb_client_SOURCES = ovsdb/ovsdb-client.c
-ovsdb_ovsdb_client_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a $(SSL_LIBS)
+ovsdb_ovsdb_client_LDADD = ovsdb/libovsdb.la lib/libopenvswitch.la $(SSL_LIBS)
 # ovsdb-client.1
 man_MANS += ovsdb/ovsdb-client.1
 DISTCLEANFILES += ovsdb/ovsdb-client.1
@@ -54,7 +58,7 @@ MAN_ROOTS += ovsdb/ovsdb-client.1.in
 # ovsdb-server
 sbin_PROGRAMS += ovsdb/ovsdb-server
 ovsdb_ovsdb_server_SOURCES = ovsdb/ovsdb-server.c
-ovsdb_ovsdb_server_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a $(SSL_LIBS)
+ovsdb_ovsdb_server_LDADD = ovsdb/libovsdb.la lib/libopenvswitch.la $(SSL_LIBS)
 # ovsdb-server.1
 man_MANS += ovsdb/ovsdb-server.1
 DISTCLEANFILES += ovsdb/ovsdb-server.1
index 760dd13..8e67c88 100644 (file)
@@ -258,11 +258,11 @@ ovsdb_clause_evaluate(const struct ovsdb_row *row,
         case OVSDB_F_LE:
         case OVSDB_F_GE:
         case OVSDB_F_GT:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
 
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 bool
index ed72ca4..807b983 100644 (file)
@@ -102,7 +102,7 @@ ovsdb_log_open(const char *name, enum ovsdb_log_open_mode open_mode,
             flags = O_RDWR | O_CREAT | O_EXCL;
         }
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
     fd = open(name, flags, 0666);
     if (fd < 0) {
index 5fd983a..967ad15 100644 (file)
@@ -150,7 +150,7 @@ ovsdb_mutation_from_json(const struct ovsdb_table_schema *ts,
         break;
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
 exit:
@@ -306,7 +306,7 @@ mutate_scalar(const struct ovsdb_type *dst_type, struct ovsdb_datum *dst,
             }
         }
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     for (i = 0; i < dst->n; i++) {
@@ -387,7 +387,7 @@ ovsdb_mutation_set_execute(struct ovsdb_row *row,
             break;
 
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
         if (error) {
             return error;
index 0cbd091..fbb7148 100644 (file)
@@ -30,6 +30,8 @@ ovsdb\-client \- command-line interface to \fBovsdb-server\fR(1)
 \fBovsdb\-client \fR[\fIoptions\fR] \fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fItable\fR
 [\fIcolumn\fR[\fB,\fIcolumn\fR]...]...
 .br
+\fBovsdb\-client \fR[\fIoptions\fR] \fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fBALL\fR
+.br
 \fBovsdb\-client help\fR
 .IP "Output formatting options:"
 [\fB\-\-format=\fIformat\fR]
@@ -128,6 +130,14 @@ line.
 If \fB\-\-detach\fR is used with \fBmonitor\fR, then \fBovsdb\-client\fR
 detaches after it has successfully received and printed the initial
 contents of \fItable\fR.
+.
+.IP "\fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fBALL\fR"
+Connects to \fIserver\fR and monitors the contents of all tables in
+\fIdatabase\fR.  Prints initial values and all kinds of changes to all
+columns in the database.  The \fB\-\-detach\fR option causes
+\fBovsdb\-client\fR to detach after it successfully receives and
+prints the initial database contents.
+.
 .SH OPTIONS
 .SS "Output Formatting Options"
 Much of the output from \fBovsdb\-client\fR is in the form of tables.
index bfc2653..f149eec 100644 (file)
 #include "ovsdb.h"
 #include "ovsdb-data.h"
 #include "ovsdb-error.h"
+#include "poll-loop.h"
 #include "sort.h"
 #include "svec.h"
 #include "stream.h"
 #include "stream-ssl.h"
 #include "table.h"
 #include "timeval.h"
+#include "unixctl.h"
 #include "util.h"
 #include "vlog.h"
 
@@ -253,6 +255,9 @@ usage(void)
            "    monitor contents of COLUMNs in TABLE in DATABASE on SERVER.\n"
            "    COLUMNs may include !initial, !insert, !delete, !modify\n"
            "    to avoid seeing the specified kinds of changes.\n"
+           "\n  monitor [SERVER] [DATABASE] ALL\n"
+           "    monitor all changes to all columns in all tables\n"
+           "    in DATBASE on SERVER.\n"
            "\n  dump [SERVER] [DATABASE]\n"
            "    dump contents of DATABASE on SERVER to stdout\n"
            "\nThe default SERVER is unix:%s/db.sock.\n"
@@ -503,6 +508,13 @@ do_transact(struct jsonrpc *rpc, const char *database OVS_UNUSED,
     putchar('\n');
     jsonrpc_msg_destroy(reply);
 }
+\f
+/* "monitor" command. */
+
+struct monitored_table {
+    struct ovsdb_table_schema *table;
+    struct ovsdb_column_set columns;
+};
 
 static void
 monitor_print_row(struct json *row, const char *type, const char *uuid,
@@ -533,31 +545,25 @@ monitor_print_row(struct json *row, const char *type, const char *uuid,
 }
 
 static void
-monitor_print(struct json *table_updates,
-              const struct ovsdb_table_schema *table,
-              const struct ovsdb_column_set *columns, bool initial)
+monitor_print_table(struct json *table_update,
+                    const struct monitored_table *mt, char *caption,
+                    bool initial)
 {
-    struct json *table_update;
+    const struct ovsdb_table_schema *table = mt->table;
+    const struct ovsdb_column_set *columns = &mt->columns;
     struct shash_node *node;
     struct table t;
     size_t i;
 
-    table_init(&t);
-    table_set_timestamp(&t, timestamp);
-
-    if (table_updates->type != JSON_OBJECT) {
-        ovs_error(0, "<table-updates> is not object");
-        return;
-    }
-    table_update = shash_find_data(json_object(table_updates), table->name);
-    if (!table_update) {
-        return;
-    }
     if (table_update->type != JSON_OBJECT) {
-        ovs_error(0, "<table-update> is not object");
+        ovs_error(0, "<table-update> for table %s is not object", table->name);
         return;
     }
 
+    table_init(&t);
+    table_set_timestamp(&t, timestamp);
+    table_set_caption(&t, caption);
+
     table_add_column(&t, "row");
     table_add_column(&t, "action");
     for (i = 0; i < columns->n_columns; i++) {
@@ -588,6 +594,30 @@ monitor_print(struct json *table_updates,
     table_destroy(&t);
 }
 
+static void
+monitor_print(struct json *table_updates,
+              const struct monitored_table *mts, size_t n_mts,
+              bool initial)
+{
+    size_t i;
+
+    if (table_updates->type != JSON_OBJECT) {
+        ovs_error(0, "<table-updates> is not object");
+        return;
+    }
+
+    for (i = 0; i < n_mts; i++) {
+        const struct monitored_table *mt = &mts[i];
+        struct json *table_update = shash_find_data(json_object(table_updates),
+                                                    mt->table->name);
+        if (table_update) {
+            monitor_print_table(table_update, mt,
+                                n_mts > 1 ? xstrdup(mt->table->name) : NULL,
+                                initial);
+        }
+    }
+}
+
 static void
 add_column(const char *server, const struct ovsdb_column *column,
            struct ovsdb_column_set *columns, struct json *columns_json)
@@ -653,7 +683,7 @@ parse_monitor_columns(char *arg, const char *server, const char *database,
         }
         free(nodes);
 
-        add_column(server, ovsdb_table_schema_get_column(table,"_version"),
+        add_column(server, ovsdb_table_schema_get_column(table, "_version"),
                    columns, columns_json);
     }
 
@@ -670,24 +700,59 @@ parse_monitor_columns(char *arg, const char *server, const char *database,
 }
 
 static void
-do_monitor(struct jsonrpc *rpc, const char *database,
-           int argc, char *argv[])
+ovsdb_client_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                  const char *argv[] OVS_UNUSED, void *exiting_)
 {
-    const char *server = jsonrpc_get_name(rpc);
-    const char *table_name = argv[0];
-    struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER;
-    struct ovsdb_table_schema *table;
-    struct ovsdb_schema *schema;
-    struct jsonrpc_msg *request;
-    struct json *monitor, *monitor_request_array,
-        *monitor_requests, *request_id;
+    bool *exiting = exiting_;
+    *exiting = true;
+    unixctl_command_reply(conn, NULL);
+}
 
-    schema = fetch_schema(rpc, database);
-    table = shash_find_data(&schema->tables, table_name);
-    if (!table) {
-        ovs_fatal(0, "%s: %s does not have a table named \"%s\"",
-                  server, database, table_name);
+static void
+ovsdb_client_block(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                   const char *argv[] OVS_UNUSED, void *blocked_)
+{
+    bool *blocked = blocked_;
+
+    if (!*blocked) {
+        *blocked = true;
+        unixctl_command_reply(conn, NULL);
+    } else {
+        unixctl_command_reply(conn, "already blocking");
     }
+}
+
+static void
+ovsdb_client_unblock(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                     const char *argv[] OVS_UNUSED, void *blocked_)
+{
+    bool *blocked = blocked_;
+
+    if (*blocked) {
+        *blocked = false;
+        unixctl_command_reply(conn, NULL);
+    } else {
+        unixctl_command_reply(conn, "already unblocked");
+    }
+}
+
+static void
+add_monitored_table(int argc, char *argv[],
+                    const char *server, const char *database,
+                    struct ovsdb_table_schema *table,
+                    struct json *monitor_requests,
+                    struct monitored_table **mts,
+                    size_t *n_mts, size_t *allocated_mts)
+{
+    struct json *monitor_request_array;
+    struct monitored_table *mt;
+
+    if (*n_mts >= *allocated_mts) {
+        *mts = x2nrealloc(*mts, allocated_mts, sizeof **mts);
+    }
+    mt = &(*mts)[(*n_mts)++];
+    mt->table = table;
+    ovsdb_column_set_init(&mt->columns);
 
     monitor_request_array = json_array_create_empty();
     if (argc > 1) {
@@ -697,58 +762,140 @@ do_monitor(struct jsonrpc *rpc, const char *database,
             json_array_add(
                 monitor_request_array,
                 parse_monitor_columns(argv[i], server, database, table,
-                                      &columns));
+                                      &mt->columns));
         }
     } else {
-        /* Allocate a writable empty string since parse_monitor_columns() is
-         * going to strtok() it and that's risky with literal "". */
+        /* Allocate a writable empty string since parse_monitor_columns()
+         * is going to strtok() it and that's risky with literal "". */
         char empty[] = "";
         json_array_add(
             monitor_request_array,
-            parse_monitor_columns(empty, server, database, table, &columns));
+            parse_monitor_columns(empty, server, database,
+                                  table, &mt->columns));
+    }
+
+    json_object_put(monitor_requests, table->name, monitor_request_array);
+}
+
+static void
+do_monitor(struct jsonrpc *rpc, const char *database,
+           int argc, char *argv[])
+{
+    const char *server = jsonrpc_get_name(rpc);
+    const char *table_name = argv[0];
+    struct unixctl_server *unixctl;
+    struct ovsdb_schema *schema;
+    struct jsonrpc_msg *request;
+    struct json *monitor, *monitor_requests, *request_id;
+    bool exiting = false;
+    bool blocked = false;
+
+    struct monitored_table *mts;
+    size_t n_mts, allocated_mts;
+
+    daemon_save_fd(STDOUT_FILENO);
+    daemonize_start();
+    if (get_detach()) {
+        int error;
+
+        error = unixctl_server_create(NULL, &unixctl);
+        if (error) {
+            ovs_fatal(error, "failed to create unixctl server");
+        }
+
+        unixctl_command_register("exit", "", 0, 0,
+                                 ovsdb_client_exit, &exiting);
+        unixctl_command_register("ovsdb-client/block", "", 0, 0,
+                                 ovsdb_client_block, &blocked);
+        unixctl_command_register("ovsdb-client/unblock", "", 0, 0,
+                                 ovsdb_client_unblock, &blocked);
+    } else {
+        unixctl = NULL;
     }
 
+    schema = fetch_schema(rpc, database);
+
     monitor_requests = json_object_create();
-    json_object_put(monitor_requests, table_name, monitor_request_array);
+
+    mts = NULL;
+    n_mts = allocated_mts = 0;
+    if (strcmp(table_name, "ALL")) {
+        struct ovsdb_table_schema *table;
+
+        table = shash_find_data(&schema->tables, table_name);
+        if (!table) {
+            ovs_fatal(0, "%s: %s does not have a table named \"%s\"",
+                      server, database, table_name);
+        }
+
+        add_monitored_table(argc, argv, server, database, table,
+                            monitor_requests, &mts, &n_mts, &allocated_mts);
+    } else {
+        size_t n = shash_count(&schema->tables);
+        const struct shash_node **nodes = shash_sort(&schema->tables);
+        size_t i;
+
+        for (i = 0; i < n; i++) {
+            struct ovsdb_table_schema *table = nodes[i]->data;
+
+            add_monitored_table(argc, argv, server, database, table,
+                                monitor_requests,
+                                &mts, &n_mts, &allocated_mts);
+        }
+        free(nodes);
+    }
 
     monitor = json_array_create_3(json_string_create(database),
                                   json_null_create(), monitor_requests);
     request = jsonrpc_create_request("monitor", monitor, NULL);
     request_id = json_clone(request->id);
     jsonrpc_send(rpc, request);
-    for (;;) {
-        struct jsonrpc_msg *msg;
-        int error;
-
-        error = jsonrpc_recv_block(rpc, &msg);
-        if (error) {
-            ovsdb_schema_destroy(schema);
-            ovs_fatal(error, "%s: receive failed", server);
-        }
 
-        if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) {
-            jsonrpc_send(rpc, jsonrpc_create_reply(json_clone(msg->params),
-                                                   msg->id));
-        } else if (msg->type == JSONRPC_REPLY
-                   && json_equal(msg->id, request_id)) {
-            monitor_print(msg->result, table, &columns, true);
-            fflush(stdout);
-            if (get_detach()) {
-                daemon_save_fd(STDOUT_FILENO);
-                daemonize();
+    for (;;) {
+        unixctl_server_run(unixctl);
+        while (!blocked) {
+            struct jsonrpc_msg *msg;
+            int error;
+
+            error = jsonrpc_recv(rpc, &msg);
+            if (error == EAGAIN) {
+                break;
+            } else if (error) {
+                ovs_fatal(error, "%s: receive failed", server);
             }
-        } else if (msg->type == JSONRPC_NOTIFY
-                   && !strcmp(msg->method, "update")) {
-            struct json *params = msg->params;
-            if (params->type == JSON_ARRAY
-                && params->u.array.n == 2
-                && params->u.array.elems[0]->type == JSON_NULL) {
-                monitor_print(params->u.array.elems[1],
-                              table, &columns, false);
+
+            if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) {
+                jsonrpc_send(rpc, jsonrpc_create_reply(json_clone(msg->params),
+                                                       msg->id));
+            } else if (msg->type == JSONRPC_REPLY
+                       && json_equal(msg->id, request_id)) {
+                monitor_print(msg->result, mts, n_mts, true);
                 fflush(stdout);
+                daemonize_complete();
+            } else if (msg->type == JSONRPC_NOTIFY
+                       && !strcmp(msg->method, "update")) {
+                struct json *params = msg->params;
+                if (params->type == JSON_ARRAY
+                    && params->u.array.n == 2
+                    && params->u.array.elems[0]->type == JSON_NULL) {
+                    monitor_print(params->u.array.elems[1], mts, n_mts, false);
+                    fflush(stdout);
+                }
             }
+            jsonrpc_msg_destroy(msg);
+        }
+
+        if (exiting) {
+            break;
+        }
+
+        jsonrpc_run(rpc);
+        jsonrpc_wait(rpc);
+        if (!blocked) {
+            jsonrpc_recv_wait(rpc);
         }
-        jsonrpc_msg_destroy(msg);
+        unixctl_server_wait(unixctl);
+        poll_block();
     }
 }
 
index 5384c32..5cec1ff 100644 (file)
@@ -35,7 +35,7 @@ traffic.
 %setup -q -n openvswitch-%{version}
 
 %build
-./configure --prefix=/usr --sysconfdir=/etc --localstatedir=%{_localstatedir} --enable-ssl %{build_number}
+./configure --prefix=/usr --sysconfdir=/etc --localstatedir=%{_localstatedir} --enable-ssl
 make %{_smp_mflags}
 
 %install
@@ -69,6 +69,9 @@ install python/compat/argparse.py $RPM_BUILD_ROOT/usr/share/openvswitch/python
 
 install -d -m 755 $RPM_BUILD_ROOT/var/lib/openvswitch
 
+# Get rid of stuff we don't want to make RPM happy.
+(cd "$RPM_BUILD_ROOT" && rm -f usr/lib/lib*)
+
 %clean
 rm -rf $RPM_BUILD_ROOT
 
index 16a8c73..ed38002 100644 (file)
@@ -34,7 +34,7 @@ traffic. This package contains the kernel modules.
 %setup -q -n openvswitch-%{version}
 
 %build
-./configure --prefix=/usr --sysconfdir=/etc --localstatedir=%{_localstatedir} --with-linux=/lib/modules/%{kernel}/build --enable-ssl %{build_number}
+./configure --prefix=/usr --sysconfdir=/etc --localstatedir=%{_localstatedir} --with-linux=/lib/modules/%{kernel}/build --enable-ssl
 make %{_smp_mflags} -C datapath/linux
 
 %install
index 02857f4..e3cb72a 100644 (file)
@@ -62,6 +62,7 @@ rm \
     $RPM_BUILD_ROOT/usr/share/man/man8/ovs-l3ping.8 \
     $RPM_BUILD_ROOT/usr/sbin/ovs-vlan-bug-workaround \
     $RPM_BUILD_ROOT/usr/share/man/man8/ovs-vlan-bug-workaround.8
+(cd "$RPM_BUILD_ROOT" && rm -rf usr/lib)
 
 install -d -m 755 $RPM_BUILD_ROOT/var/lib/openvswitch
 
index 099398a..3f5198a 100644 (file)
@@ -176,103 +176,103 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
 
 noinst_PROGRAMS += tests/test-aes128
 tests_test_aes128_SOURCES = tests/test-aes128.c
-tests_test_aes128_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_aes128_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-atomic
 tests_test_atomic_SOURCES = tests/test-atomic.c
-tests_test_atomic_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_atomic_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-bundle
 tests_test_bundle_SOURCES = tests/test-bundle.c
-tests_test_bundle_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_bundle_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-classifier
 tests_test_classifier_SOURCES = tests/test-classifier.c
-tests_test_classifier_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_classifier_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-controller
 MAN_ROOTS += tests/test-controller.8.in
 DISTCLEANFILES += utilities/test-controller.8
 noinst_man_MANS += tests/test-controller.8
 tests_test_controller_SOURCES = tests/test-controller.c
-tests_test_controller_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_controller_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-csum
 tests_test_csum_SOURCES = tests/test-csum.c
-tests_test_csum_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_csum_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-file_name
 tests_test_file_name_SOURCES = tests/test-file_name.c
-tests_test_file_name_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_file_name_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-flows
 tests_test_flows_SOURCES = tests/test-flows.c
-tests_test_flows_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_flows_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 dist_check_SCRIPTS = tests/flowgen.pl
 
 noinst_PROGRAMS += tests/test-hash
 tests_test_hash_SOURCES = tests/test-hash.c
-tests_test_hash_LDADD = lib/libopenvswitch.a
+tests_test_hash_LDADD = lib/libopenvswitch.la
 
 noinst_PROGRAMS += tests/test-heap
 tests_test_heap_SOURCES = tests/test-heap.c
-tests_test_heap_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_heap_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-hindex
 tests_test_hindex_SOURCES = tests/test-hindex.c
-tests_test_hindex_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_hindex_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-hmap
 tests_test_hmap_SOURCES = tests/test-hmap.c
-tests_test_hmap_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_hmap_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-json
 tests_test_json_SOURCES = tests/test-json.c
-tests_test_json_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_json_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-jsonrpc
 tests_test_jsonrpc_SOURCES = tests/test-jsonrpc.c
-tests_test_jsonrpc_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_jsonrpc_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-list
 tests_test_list_SOURCES = tests/test-list.c
-tests_test_list_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_list_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-lockfile
 tests_test_lockfile_SOURCES = tests/test-lockfile.c
-tests_test_lockfile_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_lockfile_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-multipath
 tests_test_multipath_SOURCES = tests/test-multipath.c
-tests_test_multipath_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_multipath_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-packets
 tests_test_packets_SOURCES = tests/test-packets.c
-tests_test_packets_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_packets_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-random
 tests_test_random_SOURCES = tests/test-random.c
-tests_test_random_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_random_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-stp
 tests_test_stp_SOURCES = tests/test-stp.c
-tests_test_stp_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_stp_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-sflow
 tests_test_sflow_SOURCES = tests/test-sflow.c
-tests_test_sflow_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_sflow_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-netflow
 tests_test_netflow_SOURCES = tests/test-netflow.c
-tests_test_netflow_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_netflow_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-unix-socket
 tests_test_unix_socket_SOURCES = tests/test-unix-socket.c
-tests_test_unix_socket_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_unix_socket_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-odp
 tests_test_odp_SOURCES = tests/test-odp.c
-tests_test_odp_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_odp_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-ovsdb
 tests_test_ovsdb_SOURCES = \
@@ -280,7 +280,7 @@ tests_test_ovsdb_SOURCES = \
        tests/idltest.c \
        tests/idltest.h
 EXTRA_DIST += tests/uuidfilt.pl tests/ovsdb-monitor-sort.pl
-tests_test_ovsdb_LDADD = ovsdb/libovsdb.a lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_ovsdb_LDADD = ovsdb/libovsdb.la lib/libopenvswitch.la $(SSL_LIBS)
 
 # idltest schema and IDL
 OVSIDL_BUILT += tests/idltest.c tests/idltest.h tests/idltest.ovsidl
@@ -294,11 +294,11 @@ tests/idltest.c: tests/idltest.h
 
 noinst_PROGRAMS += tests/test-reconnect
 tests_test_reconnect_SOURCES = tests/test-reconnect.c
-tests_test_reconnect_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_reconnect_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-sha1
 tests_test_sha1_SOURCES = tests/test-sha1.c
-tests_test_sha1_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_sha1_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-strtok_r
 tests_test_strtok_r_SOURCES = tests/test-strtok_r.c
@@ -308,19 +308,19 @@ tests_test_type_props_SOURCES = tests/test-type-props.c
 
 noinst_PROGRAMS += tests/test-util
 tests_test_util_SOURCES = tests/test-util.c
-tests_test_util_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_util_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-uuid
 tests_test_uuid_SOURCES = tests/test-uuid.c
-tests_test_uuid_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_uuid_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-vconn
 tests_test_vconn_SOURCES = tests/test-vconn.c
-tests_test_vconn_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+tests_test_vconn_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += tests/test-byte-order
 tests_test_byte_order_SOURCES = tests/test-byte-order.c
-tests_test_byte_order_LDADD = lib/libopenvswitch.a
+tests_test_byte_order_LDADD = lib/libopenvswitch.la
 
 # Python tests.
 CHECK_PYFILES = \
index ccb62b5..3723d60 100644 (file)
@@ -101,9 +101,9 @@ OVS_VSWITCHD_START(
        set bridge br-sw datapath-type=dummy \
        other-config:hwaddr=aa:55:aa:58:00:00 -- \
     add-port br-sw p1-sw -- set Interface p1-sw type=patch \
-       options:peer=p1 -- \
+       options:peer=p1 ofport_request=2 -- \
     add-port br-sw p0-sw -- set Interface p0-sw type=patch \
-       options:peer=p0 -- \
+       options:peer=p0 ofport_request=1 -- \
     add-port br-bfd1 p1 -- set Interface p1 type=patch \
        options:peer=p1-sw bfd:enable=true -- \
     add-port br-bfd0 p0 -- set Interface p0 type=patch \
@@ -269,7 +269,7 @@ OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy -- \
 ovs-appctl time/stop
 
 # wait for a while to stablize everything.
-for i in `seq 0 9`; do ovs-appctl time/warp 500; done
+for i in `seq 0 19`; do ovs-appctl time/warp 500; done
 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
 BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
 BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
@@ -277,25 +277,19 @@ BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
 
 # Test-1 BFD decay: decay to decay_min_rx
 AT_CHECK([ovs-vsctl set interface p0 bfd:decay_min_rx=3000])
-# set the bfd:decay_min_rx of p0 to 3000ms.
-BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
-BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
-
 # bfd:decay_min_rx is set to 3000ms after the local state of p0 goes up,
-# so for the first 2500ms, there should be no change.
-for i in `seq 0 4`; do ovs-appctl time/warp 500; done
-BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+# so for the first 2000ms, there should be no change.
+for i in `seq 0 3`; do ovs-appctl time/warp 500; done
 BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
 BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
 
-# advance the clock by 2000ms.
-for i in `seq 0 3`; do ovs-appctl time/warp 500; done
+# advance the clock by 5000ms.
+for i in `seq 0 9`; do ovs-appctl time/warp 500; done
 # now, min_rx should decay to 3000ms.
 BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
 BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms])
 
-# the rx_min of p0 is 3000ms now, and p1 will send next control message
-# 3000ms after decay. so, advance clock by 5000ms to make that happen.
+# advance clock by 5000ms and check the the flags are all 'none'.
 for i in `seq 0 9`; do ovs-appctl time/warp 500; done
 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
 BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
@@ -304,7 +298,7 @@ BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms])
 # End of Test-1 ###############################################################
 
 
-# Test-2 BFD decay: go back to cfg_min_rx when there is traffic
+# Test-2 BFD decay: go back to min_rx when there is traffic
 # receive packet at 1/100ms rate for 5000ms.
 for i in `seq 0 49`
 do
@@ -313,108 +307,32 @@ do
              [0], [stdout], [])
 done
 # after a decay interval (3000ms), the p0 min_rx will go back to
-# cfg_min_rx.
+# min_rx.
 BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
 BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
 # End of Test-2 ###############################################################
 
 
-# Test-3 BFD decay: go back to cfg_min_rx when decay_min_rx is changed
-# advance the clock by 5000m. p0 shoud decay.
-for i in `seq 0 9`; do ovs-appctl time/warp 500; done
-BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
-BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms])
-
-# advance the clock, to make 'flag' go back to none.
-for i in `seq 0 5`; do ovs-appctl time/warp 500; done
+# Test-3 BFD decay: set decay_min_rx to 1000ms.
+# this should firstly reset the min_rx and then re-decay to 1000ms.
+AT_CHECK([ovs-vsctl set Interface p0 bfd:decay_min_rx=1000])
+# advance the clock by 10000ms, decay should have happened.
+for i in `seq 0 19`; do ovs-appctl time/warp 500; done
 BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
 BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
-
-# change decay_min_rx to 1000ms.
-# for decay_min_rx < 2000ms, the decay detection time is set to 2000ms.
-# this should reset the min_rx.
-AT_CHECK([ovs-vsctl set Interface p0 bfd:decay_min_rx=1000])
-BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
-BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
-
-# for the following 1500ms, there should be no decay,
-# since the decay_detect_time is set to 2000ms.
-for i in `seq 0 2`
-do
-    ovs-appctl time/warp 500
-    BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
-    BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
-    BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
-    BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
-done
-
-# advance the clock by 2000ms, decay should have happened.
-for i in `seq 0 3`; do ovs-appctl time/warp 500; done
 BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
 BFD_CHECK_RX([p0], [1000ms], [1000ms], [500ms])
-# advance the clock, so 'flag' go back to none.
-for i in `seq 0 9`; do ovs-appctl time/warp 500; done
 # End of Test-3 ###############################################################
 
 
-# Test-4 BFD decay: set min_rx to 800ms.
-# this should firstly reset the min_rx and then re-decay to 1000ms.
-AT_CHECK([ovs-vsctl set Interface p0 bfd:min_rx=800])
-BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
-BFD_CHECK_RX([p0], [800ms], [800ms], [500ms])
-
-# for the following 1600ms, there should be no decay,
-# since the decay detection time is set to 2000ms.
-for i in `seq 0 1`
-do
-    ovs-appctl time/warp 800
-    BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
-    BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
-    BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
-    BFD_CHECK_RX([p0], [800ms], [800ms], [500ms])
-done
-
-# advance the clock by 2000ms, decay should have happened.
-for i in `seq 0 3`; do ovs-appctl time/warp 500; done
-BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
-BFD_CHECK_RX([p0], [1000ms], [1000ms], [500ms])
-# advance the clock, so 'flag' go back to none.
-for i in `seq 0 9`; do ovs-appctl time/warp 500; done
-# End of Test-4 ###############################################################
-
-
-# Test-5 BFD decay: set min_rx to 300ms and decay_min_rx to 5000ms together.
-AT_CHECK([ovs-vsctl set Interface p0 bfd:min_rx=300 bfd:decay_min_rx=5000])
-BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
-BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
-
-# for decay_min_rx > 2000ms, the decay detection time is set to
-# decay_min_rx (5000ms).
-# for the following 4500ms, there should be no decay,
-# since the decay detection time is set to 5000ms.
-for i in `seq 0 8`
-do
-    ovs-appctl time/warp 500
-    BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
-    BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
-    BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
-    BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
-done
-
-# advance the clock by 2000ms, decay should have happened.
-for i in `seq 0 3`; do ovs-appctl time/warp 500; done
-BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
-BFD_CHECK_RX([p0], [5000ms], [5000ms], [500ms])
-# advance the clock, to make 'flag' go back to none.
-for i in `seq 0 9`; do ovs-appctl time/warp 500; done
-# End of Test-5 ###############################################################
-
-
-# Test-6 BFD decay: set decay_min_rx to 0 to disable bfd decay.
+# Test-4 BFD decay: set decay_min_rx to 0 to disable bfd decay.
 AT_CHECK([ovs-vsctl set Interface p0 bfd:decay_min_rx=0])
+# advance the clock by 5000ms.
+for i in `seq 0 9`; do ovs-appctl time/warp 500; done
 # min_rx is reset.
 BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
 BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
+
 for i in `seq 0 20`
 do
     ovs-appctl time/warp 500
@@ -423,18 +341,20 @@ do
     BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
     BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
 done
-# End of Test-6 ################################################################
+# End of Test-4 ################################################################
 
 
-# Test-7 BFD decay: rmt_min_tx is greater than decay_min_rx
+# Test-5 BFD decay: rmt_min_tx is greater than decay_min_rx
 AT_CHECK([ovs-vsctl set Interface p0 bfd:decay_min_rx=3000 -- set interface p1 bfd:min_tx=5000])
-# there will be poll sequences from both sides. and it is hard to determine the
-# order. so just skip 10000ms and check the RX/TX. at that time, p0 should be in decay already.
+# advance the clock by 10000ms to stable everything.
 for i in `seq 0 19`; do ovs-appctl time/warp 500; done
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+# p0 rx should show 5000ms even if it is in decay.
 BFD_CHECK_TX([p0], [500ms], [300ms], [5000ms])
 BFD_CHECK_RX([p0], [5000ms], [3000ms], [500ms])
 # then, there should be no change of status,
-for i in `seq 0 9`
+for i in `seq 0 19`
 do
     ovs-appctl time/warp 500
     BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
@@ -444,17 +364,19 @@ do
 done
 # reset the p1's min_tx to 500ms.
 AT_CHECK([ovs-vsctl set Interface p1 bfd:min_tx=500])
+# advance the clock by 20000ms to stable everything.
 # since p0 has been in decay, now the RX will show 3000ms.
+for i in `seq 0 39`; do ovs-appctl time/warp 500; done
 BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
 BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms])
-# End of Test-7 ###############################################################
+# End of Test-5 ###############################################################
 
 
-# Test-8 BFD decay: state up->down->up.
+# Test-6 BFD decay: state up->down->up.
 # turn bfd off on p1
 AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false])
 
-# check the state change of bfd on p0. After 15000 ms (> 3 min_rx intervals)
+# advance the clock by 15000ms to stable everything.
 for i in `seq 0 14`; do ovs-appctl time/warp 1000; done
 BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
 BFD_CHECK_TX([p0], [1000ms], [1000ms], [0ms])
@@ -470,7 +392,7 @@ BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
 for i in `seq 0 9`; do ovs-appctl time/warp 500; done
 BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
 BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms])
-# End of Test-8 ################################################################
+# End of Test-6 ################################################################
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -629,8 +551,6 @@ done
 
 # reconfigure the decay_min_rx to 1000ms.
 AT_CHECK([ovs-vsctl set interface p0 bfd:decay_min_rx=1000])
-BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
-BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
 
 # wait for 5000ms to decay.
 for i in `seq 0 9`; do ovs-appctl time/warp 500; done
index 9e351d0..7ab4354 100644 (file)
@@ -83,17 +83,54 @@ for i in `seq 0 100`; do ovs-appctl time/warp 100; done
 CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [100ms], [2], [up])
 CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [100ms], [1], [up])
 
-# turn cfm on p1 off, should increment the cfm_flap_count on p1.
+# turn cfm on p1 off, should increment the cfm_flap_count on p0.
 AT_CHECK([ovs-vsctl remove interface p1 cfm_mpid 2])
 for i in `seq 0 10`; do ovs-appctl time/warp 100; done
 CFM_VSCTL_LIST_IFACE([p0], [cfm_flap_count], [cfm_flap_count      : 1])
 CFM_VSCTL_LIST_IFACE([p1], [cfm_flap_count], [cfm_flap_count      : [[]]])
 
-# turn cfm on p1 on again, should increment the cfm_flap_count on p1.
+# turn cfm on p1 on again, should increment the cfm_flap_count on p0.
 AT_CHECK([ovs-vsctl set interface p1 cfm_mpid=2])
 for i in `seq 0 10`; do ovs-appctl time/warp 100; done
 CFM_VSCTL_LIST_IFACE([p0], [cfm_flap_count], [cfm_flap_count      : 2])
-CFM_VSCTL_LIST_IFACE([p1], [cfm_flap_count], [cfm_flap_count      : 0])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([cfm - fault_override])
+OVS_VSWITCHD_START([add-br br1 -- \
+                    set bridge br1 datapath-type=dummy \
+                    other-config:hwaddr=aa:55:aa:56:00:00 -- \
+                    add-port br1 p1 -- set Interface p1 type=patch \
+                    options:peer=p0 -- \
+                    add-port br0 p0 -- set Interface p0 type=patch \
+                    options:peer=p1 -- \
+                    set Interface p0 cfm_mpid=1 other_config:cfm_interval=100 other_config:cfm_extended=true -- \
+                    set Interface p1 cfm_mpid=2 other_config:cfm_interval=100 other_config:cfm_extended=true])
+
+ovs-appctl time/stop
+# wait for a while to stablize cfm.
+for i in `seq 0 100`; do ovs-appctl time/warp 100; done
+CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [100ms], [2], [up])
+CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [100ms], [1], [up])
+AT_CHECK([ovs-appctl cfm/show p1 | grep 'fault_override'], [1], [ignore])
+CFM_VSCTL_LIST_IFACE([p1], [cfm_fault_status], [cfm_fault_status    : [[]]])
+
+# set a fault and see that this is shown in the CFM module and the database
+AT_CHECK([ovs-appctl cfm/set-fault p1 true], [0], [OK
+])
+AT_CHECK([ovs-appctl time/warp 100], [0], [ignore])
+AT_CHECK([ovs-appctl cfm/show p1 | grep 'fault_override' | sed -e 's/MPID [[0-9]]*: extended //'], [0], [dnl
+fault_override
+])
+CFM_VSCTL_LIST_IFACE([p1], [cfm_fault_status], [cfm_fault_status    : [[override]]])
+
+# reset and see that it returned to normal
+AT_CHECK([ovs-appctl cfm/set-fault normal], [0], [OK
+])
+AT_CHECK([ovs-appctl time/warp 100], [0], [ignore])
+AT_CHECK([ovs-appctl cfm/show p1 | grep 'fault_override'], [1], [ignore])
+CFM_VSCTL_LIST_IFACE([p1], [cfm_fault_status], [cfm_fault_status    : [[]]])
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
\ No newline at end of file
index 546c8f7..0015544 100644 (file)
@@ -60,3 +60,50 @@ Datapath actions: 2
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_BANNER([flow classifier prefix lookup])
+AT_SETUP([flow classifier - prefix lookup])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2], [3])
+AT_CHECK([ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- --id=@N1 create Flow_Table name=t0], [0], [ignore], [])
+AT_CHECK([ovs-vsctl set Flow_Table t0 prefixes=ipv6_label], [0])
+AT_CHECK([ovs-vsctl set Flow_Table t0 prefixes=nw_dst,nw_src,tun_dst,tun_src], [1], [],
+[ovs-vsctl: nw_dst,nw_src,tun_dst,tun_src: 4 value(s) specified but the maximum number is 3
+])
+AT_CHECK([ovs-vsctl set Flow_Table t0 prefixes=nw_dst,nw_dst], [1], [],
+[ovs-vsctl: nw_dst,nw_dst: set contains duplicate value
+])
+AT_CHECK([ovs-vsctl set Flow_Table t0 prefixes=nw_dst], [0])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1 priority=16,tcp,nw_dst=10.1.0.0/255.255.0.0,action=output(3)
+table=0 in_port=1 priority=32,tcp,nw_dst=10.1.2.0/255.255.255.0,tp_src=79,action=output(2)
+table=0 in_port=1 priority=33,tcp,nw_dst=10.1.2.15,tp_dst=80,action=drop
+table=0 in_port=1 priority=0,ip,action=drop
+table=0 in_port=2 priority=16,tcp,nw_dst=192.168.0.0/255.255.0.0,action=output(1)
+table=0 in_port=2 priority=0,ip,action=drop
+table=0 in_port=3 priority=16,tcp,nw_src=10.1.0.0/255.255.0.0,action=output(1)
+table=0 in_port=3 priority=0,ip,action=drop
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout])
+AT_CHECK([tail -2 stdout], [0],
+  [Relevant fields: skb_priority=0,tcp,in_port=1,nw_dst=192.168.0.0/16,nw_frag=no
+Datapath actions: drop
+])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=2,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout])
+AT_CHECK([tail -2 stdout], [0],
+  [Relevant fields: skb_priority=0,tcp,in_port=2,nw_dst=192.168.0.0/16,nw_frag=no
+Datapath actions: 1
+])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=10.1.2.15,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout])
+AT_CHECK([tail -2 stdout], [0],
+  [Relevant fields: skb_priority=0,tcp,in_port=1,nw_dst=10.1.2.15,nw_frag=no,tp_dst=80
+Datapath actions: drop
+])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=10.1.2.15,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=79'], [0], [stdout])
+AT_CHECK([tail -2 stdout], [0],
+  [Relevant fields: skb_priority=0,tcp,in_port=1,nw_dst=10.1.2.15,nw_frag=no,tp_src=8,tp_dst=79
+Datapath actions: 3
+])
+OVS_VSWITCHD_STOP(["/'prefixes' with incompatible field: ipv6_label/d"])
+AT_CLEANUP
index 9bc7810..cafa8df 100644 (file)
@@ -208,6 +208,7 @@ fi
 AT_CLEANUP
 
 AT_SETUP([daemon --detach --monitor closes standard fds])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
 AT_CAPTURE_FILE([pid])
 AT_CAPTURE_FILE([status])
 AT_CAPTURE_FILE([stderr])
index ffc8996..d44bee0 100644 (file)
@@ -6,6 +6,11 @@ OVS_VSWITCHD_START([\
         set Port p1 lacp=active --\
         set Interface p1 type=dummy ])
 
+ovs-appctl time/stop
+ovs-appctl time/warp 100
+ovs-appctl time/warp 100
+ovs-appctl time/warp 100
+
 AT_CHECK([ovs-appctl lacp/show], [0], [dnl
 ---- p1 ----
        status: active negotiated
@@ -53,6 +58,11 @@ OVS_VSWITCHD_START([dnl
             other_config:lacp-port-priority=222 \
             other_config:lacp-aggregation-key=3333 ])
 
+ovs-appctl time/stop
+ovs-appctl time/warp 100
+ovs-appctl time/warp 100
+ovs-appctl time/warp 100
+
 AT_CHECK([ovs-appctl lacp/show], [0], [stdout])
 AT_CHECK([sed -e 's/aggregation key:.*/aggregation key: <omitted>/' < stdout], [0], [dnl
 ---- bond ----
@@ -126,8 +136,10 @@ OVS_VSWITCHD_START(
   [add-bond br0 bond0 p0 p1 bond_mode=balance-tcp lacp=active \
                             other-config:lacp-time=fast \
                             other-config:bond-rebalance-interval=0 -- \
-   set interface p0 type=patch options:peer=p2 ofport_request=1 -- \
-   set interface p1 type=patch options:peer=p3 ofport_request=2 -- \
+   set interface p0 type=patch options:peer=p2 ofport_request=1 \
+                    other-config:lacp-aggregation-key=2 -- \
+   set interface p1 type=patch options:peer=p3 ofport_request=2 \
+                    other-config:lacp-aggregation-key=2 -- \
    add-br br1 -- \
    set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \
    set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \
@@ -135,8 +147,10 @@ OVS_VSWITCHD_START(
    add-bond br1 bond1 p2 p3 bond_mode=balance-tcp lacp=active \
                             other-config:lacp-time=fast \
                             other-config:bond-rebalance-interval=0 -- \
-   set interface p2 type=patch options:peer=p0 ofport_request=3 -- \
-   set interface p3 type=patch options:peer=p1 ofport_request=4 --])
+   set interface p2 type=patch options:peer=p0 ofport_request=3 \
+                    other-config:lacp-aggregation-key=4 -- \
+   set interface p3 type=patch options:peer=p1 ofport_request=4 \
+                    other-config:lacp-aggregation-key=4 --])
 
 AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
 ])
index 66343d3..1c14c84 100644 (file)
@@ -293,6 +293,9 @@ AT_CHECK([[ovs-ofctl add-flow br0 'actions=load:3->NXM_NX_REG0[0..15],learn(tabl
 for i in 1 2 3 4 5 6 7 8 9 10; 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)'
     ovs-appctl time/warp 10
+    if [[ $i -eq 1 ]]; then
+        sleep 1
+    fi
 done
 
 # Check for the learning entry.
index 57cdd6c..b0ccd40 100644 (file)
@@ -181,3 +181,7 @@ AT_CHECK([sed 's/.*: //
 ])
 
 AT_CLEANUP
+
+AT_SETUP([snprintf])
+AT_CHECK([test-util snprintf])
+AT_CLEANUP
index 2f09c1b..989272c 100644 (file)
@@ -558,7 +558,7 @@ AT_CHECK([ovs-ofctl ofp-print "\
 30 e0 35 00 00 05 00 00 00 00 00 00 00 00 00 01 \
 00 00 00 00 00 00 00 3c \
 "], [0], [dnl
-OFPT_FLOW_REMOVED (xid=0x0): priority=65535,arp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 reason=idle duration5.82s idle5 pkts1 bytes60
+OFPT_FLOW_REMOVED (xid=0x0): priority=65535,arp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 reason=idle duration5.820s idle5 pkts1 bytes60
 ])
 AT_CLEANUP
 
@@ -569,7 +569,7 @@ AT_CHECK([ovs-ofctl ofp-print "\
 80 00 01 05 00 00 00 01 00 98 96 80 00 3c 00 78 \
 00 00 00 00 00 12 d6 87 00 00 00 00 6f 68 ba 66 \
 00 01 00 0a 80 00 0c 02 10 09 00 00 00 00 00 00"], [0], [dnl
-OFPT_FLOW_REMOVED (OF1.2) (xid=0x0): dl_vlan=9 reason=hard table_id=5 cookie:0xfedcba9876543210 duration1.01s idle60 hard120 pkts1234567 bytes1869134438
+OFPT_FLOW_REMOVED (OF1.2) (xid=0x0): dl_vlan=9 reason=hard table_id=5 cookie:0xfedcba9876543210 duration1.010s idle60 hard120 pkts1234567 bytes1869134438
 ])
 AT_CLEANUP
 
@@ -580,7 +580,7 @@ AT_CHECK([ovs-ofctl ofp-print "\
 80 00 01 05 00 00 00 01 00 98 96 80 00 3c 00 78 \
 00 00 00 00 00 12 d6 87 00 00 00 00 6f 68 ba 66 \
 00 01 00 0a 80 00 0c 02 10 09 00 00 00 00 00 00"], [0], [dnl
-OFPT_FLOW_REMOVED (OF1.3) (xid=0x0): dl_vlan=9 reason=hard table_id=5 cookie:0xfedcba9876543210 duration1.01s idle60 hard120 pkts1234567 bytes1869134438
+OFPT_FLOW_REMOVED (OF1.3) (xid=0x0): dl_vlan=9 reason=hard table_id=5 cookie:0xfedcba9876543210 duration1.010s idle60 hard120 pkts1234567 bytes1869134438
 ])
 AT_CLEANUP
 
@@ -1192,9 +1192,9 @@ c0 a8 00 02 00 08 00 00 00 00 00 09 05 b8 d8 00 \
 00 00 00 00 00 00 00 00 \
 "], [0], [dnl
 OFPST_FLOW reply (xid=0x4):
- cookie=0x0, duration=4.2s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,arp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 actions=output:1
- cookie=0x0, duration=8.9s, table=0, n_packets=13, n_bytes=1274, idle_timeout=5, priority=65535,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,icmp_type=0,icmp_code=0 actions=output:3
- cookie=0x0, duration=4.28s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=1,nw_tos=0,tp_src=0,tp_dst=0 actions=output:3
+ cookie=0x0, duration=4.200s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,arp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 actions=output:1
+ cookie=0x0, duration=8.900s, table=0, n_packets=13, n_bytes=1274, idle_timeout=5, priority=65535,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,icmp_type=0,icmp_code=0 actions=output:3
+ cookie=0x0, duration=4.280s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=1,nw_tos=0,tp_src=0,tp_dst=0 actions=output:3
  cookie=0x0, duration=9.096s, table=0, n_packets=13, n_bytes=1274, idle_timeout=5, icmp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,icmp_type=8,icmp_code=0 actions=output:1
  cookie=0x0, duration=0s, table=2, n_packets=0, n_bytes=0, actions=drop
 ])
@@ -1718,11 +1718,11 @@ AT_CHECK([ovs-ofctl ofp-print "\
 ff ff ff ff ff ff ff ff \
 "], [0], [dnl
 OFPST_QUEUE reply (OF1.3) (xid=0x1): 6 queues
-  port 3 queue 1: bytes=302, pkts=1, errors=0, duration=100.5s
-  port 3 queue 2: bytes=0, pkts=0, errors=0, duration=100.5s
-  port 2 queue 1: bytes=2100, pkts=20, errors=0, duration=100.5s
-  port 2 queue 2: bytes=0, pkts=0, errors=0, duration=100.5s
-  port 1 queue 1: bytes=0, pkts=0, errors=0, duration=100.5s
+  port 3 queue 1: bytes=302, pkts=1, errors=0, duration=100.500s
+  port 3 queue 2: bytes=0, pkts=0, errors=0, duration=100.500s
+  port 2 queue 1: bytes=2100, pkts=20, errors=0, duration=100.500s
+  port 2 queue 2: bytes=0, pkts=0, errors=0, duration=100.500s
+  port 1 queue 1: bytes=0, pkts=0, errors=0, duration=100.500s
   port 1 queue 2: bytes=0, pkts=0, errors=0, duration=?
 ])
 AT_CLEANUP
@@ -1773,8 +1773,8 @@ AT_CHECK([ovs-ofctl ofp-print "\
 00 00 00 00 00 00 11 11 00 00 00 00 00 22 22 22 \
 "], [0], [dnl
 OFPST_GROUP reply (OF1.3) (xid=0x2):
- group_id=2271560481,duration=18.5s,ref_count=4,packet_count=34952,byte_count=7829367,bucket0:packet_count=4369,byte_count=2236962,bucket1:packet_count=4369,byte_count=2236962,bucket2:packet_count=26214,byte_count=3355443
- group_id=5,duration=16.5s,ref_count=2,packet_count=34952,byte_count=7829367,bucket0:packet_count=4369,byte_count=2236962,bucket1:packet_count=4369,byte_count=2236962
+ group_id=2271560481,duration=18.500s,ref_count=4,packet_count=34952,byte_count=7829367,bucket0:packet_count=4369,byte_count=2236962,bucket1:packet_count=4369,byte_count=2236962,bucket2:packet_count=26214,byte_count=3355443
+ group_id=5,duration=16.500s,ref_count=2,packet_count=34952,byte_count=7829367,bucket0:packet_count=4369,byte_count=2236962,bucket1:packet_count=4369,byte_count=2236962
 ])
 AT_CLEANUP
 
@@ -2521,20 +2521,20 @@ ff ff 00 18 00 00 23 20 00 07 00 1f 00 01 00 04 \
 "], [0],
 [[NXST_FLOW reply (xid=0x4):
  cookie=0x0, duration=1.048s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2535,tp_dst=0 actions=output:1
- cookie=0x0, duration=3.84s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, idle_age=2, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2532,tp_dst=0 actions=output:1
+ cookie=0x0, duration=3.840s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, idle_age=2, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2532,tp_dst=0 actions=output:1
  cookie=0x0, duration=2.872s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, idle_age=4, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2533 actions=output:3
  cookie=0x0, duration=4.756s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, idle_age=0, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2531,tp_dst=0 actions=output:1
- cookie=0x0, duration=2.88s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, hard_timeout=10, idle_age=2, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2533,tp_dst=0 actions=output:1
+ cookie=0x0, duration=2.880s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, hard_timeout=10, idle_age=2, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2533,tp_dst=0 actions=output:1
  cookie=0x0, duration=5.672s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2530,tp_dst=0 actions=output:1
- cookie=0x0, duration=1.04s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2535 actions=output:3
+ cookie=0x0, duration=1.040s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2535 actions=output:3
  cookie=0x0, duration=1.952s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2534 actions=output:3
  cookie=0x0, duration=4.668s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2531 actions=output:3
  cookie=0x0, duration=3.752s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2532 actions=output:3
  cookie=0x0, duration=0.172s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2536,tp_dst=0 actions=output:1
  cookie=0x0, duration=5.624s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2530 actions=output:3
- cookie=0x0, duration=0.08s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2536 actions=output:3
- cookie=0x0, duration=1.96s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2534,tp_dst=0 actions=output:1
- cookie=0x0, duration=228.78s, table=0, n_packets=0, n_bytes=0, reg0=0x7b,tun_id=0x1c8 actions=load:0x5->NXM_NX_REG0[]
+ cookie=0x0, duration=0.080s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2536 actions=output:3
+ cookie=0x0, duration=1.960s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2534,tp_dst=0 actions=output:1
+ cookie=0x0, duration=228.780s, table=0, n_packets=0, n_bytes=0, reg0=0x7b,tun_id=0x1c8 actions=load:0x5->NXM_NX_REG0[]
  cookie=0x0, duration=3600.0005s, table=1, n_packets=100, n_bytes=6400, actions=drop
 ]])
 AT_CLEANUP
index 4d8d460..730bb91 100644 (file)
@@ -148,7 +148,7 @@ AT_CLEANUP
 AT_SETUP([ofproto-dpif - select group with weight])
 OVS_VSWITCHD_START
 ADD_OF_PORTS([br0], [1], [10], [11], [12])
-AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=select,bucket=output:10,bucket=output:11,weight=2,bucket=output:12,weight=0'])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=select,bucket=output:10,bucket=output:11,weight=2000,bucket=output:12,weight=0'])
 AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)'])
 AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:07,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
@@ -2100,22 +2100,19 @@ for delay in 1000 30000; do
     ovs-appctl time/warp $delay
 done
 
+ovs-appctl time/warp 6000
 sleep 1
 OVS_VSWITCHD_STOP
 ovs-appctl -t test-netflow exit
 
-AT_CHECK([[sed -e 's/, uptime [0-9]*//
-s/, now [0-9.]*//
-s/time \([0-9]*\)\.\.\.\1$/time <moment>/
-s/time [0-9]*\.\.\.[0-9]*/time <range>/
-' netflow.log | sort]], [0],
-  [
-header: v5, seq 0, engine 2,1
-header: v5, seq 1, engine 2,1
-seq 0: 192.168.0.1 > 192.168.0.2, if 1 > 65535, 1 pkts, 60 bytes, ICMP 8:0, time <moment>
-seq 1: 192.168.0.1 > 192.168.0.2, if 1 > 2, 1 pkts, 60 bytes, ICMP 8:0, time <moment>
-seq 1: 192.168.0.2 > 192.168.0.1, if 2 > 1, 2 pkts, 120 bytes, ICMP 0:0, time <range>
-])
+AT_CHECK([test `grep "192.168.0.1 > 192.168.0.2, if 1 > 65535, 1 pkts, 60 bytes, ICMP 8:0" netflow.log | wc -l` -eq 1])
+
+AT_CHECK([test `grep "192.168.0.1 > 192.168.0.2, if 1 > 2, 1 pkts, 60 bytes, ICMP 8:0" netflow.log | wc -l` -eq 1])
+
+combined=`grep "192.168.0.2 > 192.168.0.1, if 2 > 1, 2 pkts, 120 bytes, ICMP 0:0" netflow.log | wc -l`
+separate=`grep "192.168.0.2 > 192.168.0.1, if 2 > 1, 1 pkts, 60 bytes, ICMP 0:0" netflow.log | wc -l`
+AT_CHECK([test $separate = 2 || test $combined = 1], [0])
+
 AT_CLEANUP
 
 dnl Test that basic NetFlow reports active expirations correctly.
@@ -2192,12 +2189,6 @@ done < netflow.log
 AT_CHECK([echo $n_learn $n_in $n_out $n_other], [0], [1 59 60 0
 ])
 
-# There should be 1 expiration for MAC learning,
-# at least 5 active and a final expiration in one direction,
-# and at least 5 active and a final expiration in the other direction.
-echo $n_recs
-AT_CHECK([test $n_recs -ge 13])
-
 AT_CLEANUP
 
 AT_SETUP([idle_age and hard_age increase over time])
@@ -2347,12 +2338,11 @@ ADD_OF_PORTS([br1], [3])
 
 AT_CHECK([ovs-appctl dpif/show], [0], [dnl
 dummy@ovs-dummy: hit:0 missed:0
-       flows: cur: 0, avg: 0, max: 0, life span: 0ms
-       br0: hit:0 missed:0
+       br0:
                br0 65534/100: (dummy)
                p1 1/1: (dummy)
                p2 2/2: (dummy)
-       br1: hit:0 missed:0
+       br1:
                br1 65534/101: (dummy)
                p3 3/3: (dummy)
 ])
@@ -2370,42 +2360,21 @@ 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
-skb_priority(0),in_port(1),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/0xff), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
-skb_priority(0),in_port(2),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/0xff), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
+skb_priority(0),in_port(1),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/0xff), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(controller))
+skb_priority(0),in_port(2),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/0xff), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(controller))
 ])
 
 AT_CHECK([ovs-appctl dpif/dump-flows br1 | sort | STRIP_USED], [0], [dnl
-skb_priority(0),in_port(3),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/0xff), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
+skb_priority(0),in_port(3),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/0xff), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(controller))
 ])
 
-OVS_VSWITCHD_STOP
-AT_CLEANUP
-
-AT_SETUP([ofproto-dpif - ovs-appctl dpif/del-flows])
-OVS_VSWITCHD_START([add-br br1 -- \
-                    set bridge br1 datapath-type=dummy fail-mode=secure])
-ADD_OF_PORTS([br0], [1], [2])
-ADD_OF_PORTS([br1], [3])
-
-AT_CHECK([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=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl netdev-dummy/receive p2 '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)'])
-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
-skb_priority(0),in_port(1),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/0xff), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
-skb_priority(0),in_port(2),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/0xff), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
+AT_CHECK([ovs-appctl dpif/dump-flows -m br0 | sort | STRIP_USED], [0], [dnl
+skb_priority(0),skb_mark(0/0),in_port(p1),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/0xff),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(controller))
+skb_priority(0),skb_mark(0/0),in_port(p2),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/0xff),icmp(type=0/0,code=0/0), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(controller))
 ])
 
-AT_CHECK([ovs-appctl dpif/dump-flows br1 | sort | STRIP_USED], [0], [dnl
-skb_priority(0),in_port(3),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/0xff), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
-])
-
-AT_CHECK([ovs-appctl dpif/del-flows br0])
-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
-skb_priority(0),in_port(3),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/0xff), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
+AT_CHECK([ovs-appctl dpif/dump-flows -m br1 | sort | STRIP_USED], [0], [dnl
+skb_priority(0),skb_mark(0/0),in_port(p3),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/0xff),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(controller))
 ])
 
 OVS_VSWITCHD_STOP
@@ -2420,17 +2389,23 @@ OVS_VSWITCHD_START([add-br br1 \
 ADD_OF_PORTS([br0], [2])
 ADD_OF_PORTS([br1], [3])
 
-AT_CHECK([ovs-appctl time/stop]) dnl Make life span averages consistent.
+AT_CHECK([ovs-appctl time/stop])
 
 AT_CHECK([ovs-ofctl add-flow br0 actions=LOCAL,output:1,output:2])
 AT_CHECK([ovs-ofctl add-flow br1 actions=LOCAL,output:1,output:3])
 
 for i in $(seq 1 10); do
     ovs-appctl netdev-dummy/receive br0 '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)'
+    if [[ $i -eq 1 ]]; then
+        sleep 1
+    fi
 done
 
 for i in $(seq 1 5); do
     ovs-appctl netdev-dummy/receive br1 '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)'
+    if [[ $i -eq 1 ]]; then
+        sleep 1
+    fi
 done
 
 AT_CHECK([ovs-appctl time/warp 1000 && ovs-appctl time/warp 1000], [0], [warped
@@ -2439,12 +2414,11 @@ warped
 
 AT_CHECK([ovs-appctl dpif/show], [0], [dnl
 dummy@ovs-dummy: hit:13 missed:2
-       flows: cur: 2, avg: 1, max: 2, life span: 1250ms
-       br0: hit:9 missed:1
+       br0:
                br0 65534/100: (dummy)
                p2 2/2: (dummy)
                pbr0 1/none: (patch: peer=pbr1)
-       br1: hit:4 missed:1
+       br1:
                br1 65534/101: (dummy)
                p3 3/3: (dummy)
                pbr1 1/none: (patch: peer=pbr0)
@@ -2472,34 +2446,6 @@ OFPST_PORT reply (xid=0x4): 1 ports
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto-dpif - ovs-appctl dpif/show rates])
-OVS_VSWITCHD_START([set Bridge br0 fail-mode=secure])
-ADD_OF_PORTS([br0], 1, 2)
-
-AT_CHECK([ovs-appctl time/stop]) dnl Make life span averages consistent.
-AT_CHECK([ovs-ofctl add-flow br0 actions=LOCAL,output:1,output:2])
-
-for i in $(seq 1 61); do
-    ovs-appctl netdev-dummy/receive br0 '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)'
-    ovs-appctl time/warp 10000
-    ovs-appctl time/warp 50000
-done
-
-AT_CHECK([ovs-appctl time/warp 10000], [0], [warped
-])
-
-AT_CHECK([ovs-appctl dpif/show | sed 's/ 10[[0-9]]\{3\}(ms)$/ 10000(ms)/'], [0], [dnl
-dummy@ovs-dummy: hit:0 missed:61
-       flows: cur: 0, avg: 0, max: 1, life span: 1666ms
-       br0: hit:0 missed:61
-               br0 65534/100: (dummy)
-               p1 1/1: (dummy)
-               p2 2/2: (dummy)
-])
-
-OVS_VSWITCHD_STOP
-AT_CLEANUP
-
 AT_SETUP([ofproto-dpif - port duration])
 OVS_VSWITCHD_START([set Bridge br0 protocols=OpenFlow13])
 ADD_OF_PORTS([br0], 1, 2)
@@ -2508,17 +2454,10 @@ ovs-appctl time/stop
 ovs-appctl time/warp 10000
 
 AT_CHECK([ovs-ofctl -O openflow13 dump-ports br0], [0], [stdout])
-AT_CHECK([sed 's/=[[0-9]][[0-9]]\(\.[[0-9]][[0-9]]*\)\{0,1\}s/=?s/' stdout], [0],
+AT_CHECK([sed -n 's/=[[0-9]][[0-9]]\(\.[[0-9]][[0-9]]*\)\{0,1\}s/=?s/p' stdout], [0],
 [dnl
-OFPST_PORT reply (OF1.3) (xid=0x2): 3 ports
-  port  1: rx pkts=0, bytes=0, drop=0, errs=0, frame=0, over=0, crc=0
-           tx pkts=0, bytes=0, drop=0, errs=0, coll=0
            duration=?s
-  port  2: rx pkts=0, bytes=0, drop=0, errs=0, frame=0, over=0, crc=0
-           tx pkts=0, bytes=0, drop=0, errs=0, coll=0
            duration=?s
-  port LOCAL: rx pkts=0, bytes=0, drop=0, errs=0, frame=0, over=0, crc=0
-           tx pkts=0, bytes=0, drop=0, errs=0, coll=0
            duration=?s
 ])
 OVS_VSWITCHD_STOP
@@ -2564,8 +2503,8 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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 netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,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/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,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/0xff), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2573,6 +2512,7 @@ AT_CLEANUP
 AT_SETUP([ofproto-dpif megaflow - L3 classification])
 OVS_VSWITCHD_START
 ADD_OF_PORTS([br0], [1], [2])
+AT_CHECK([ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- --id=@N1 create Flow_Table name=t0 prefixes=nw_dst,nw_src], [0], [ignore], [])
 AT_DATA([flows.txt], [dnl
 table=0 in_port=1,icmp,nw_src=10.0.0.4 actions=output(2)
 ])
@@ -2580,8 +2520,25 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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 netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
-skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/0.0.0.0,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/0.0.0.0,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.252,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/0.0.0.0,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - IPv6 classification])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_CHECK([ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- --id=@N1 create Flow_Table name=t0 prefixes=ipv6_dst,ipv6_src], [0], [ignore], [])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1,ipv6,ipv6_src=2001:db8:3c4d:1:2:3:4:5 actions=output(2)
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x86dd),ipv6(src=2001:db8:3c4d:1:2:3:4:5,dst=fe80::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x86dd),ipv6(src=2001:db8:3c4d:5:4:3:2:1,dst=2001:db8:3c4d:1:2:3:4:1,label=0,proto=99,tclass=0x70,hlimit=64,frag=no)'])
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth_type(0x86dd),ipv6(src=2001:db8:3c4d:1:2:3:4:5/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,dst=fe80::2/::,label=0/0,proto=10/0,tclass=0x70/0,hlimit=128/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(1),eth_type(0x86dd),ipv6(src=2001:db8:3c4d:5:4:3:2:1/ffff:ffff:ffff:fffc::,dst=2001:db8:3c4d:1:2:3:4:1/::,label=0/0,proto=99/0,tclass=0x70/0,hlimit=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2608,8 +2565,8 @@ AT_CHECK([ovs-ofctl add-flow br0 action=normal])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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 netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),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/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),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/0xff), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2625,8 +2582,8 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0a),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1)'])
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1), packets:0, bytes:0, used:never, actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2649,8 +2606,8 @@ AT_CHECK([ovs-ofctl add-flow br0 action=normal])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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 netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/255.255.255.255,proto=1/0xff,tos=0/0xfc,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/255.255.255.255,proto=1/0xff,tos=0/0xfc,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/255.255.255.255,proto=1/0xff,tos=0/0xfc,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/255.255.255.255,proto=1/0xff,tos=0/0xfc,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:never, actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2668,8 +2625,8 @@ AT_CHECK([ovs-ofctl add-flow br0 action=normal])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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 netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),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/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),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/0xff), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2687,8 +2644,8 @@ AT_CHECK([ovs-ofctl add-flow br0 action=normal])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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 netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),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/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),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/0xff), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2724,8 +2681,8 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(1),eth(src=50:54:00:00:00:
 AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
-skb_priority(0),in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/255.255.255.255,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(7),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/255.255.255.255,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/255.255.255.255,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(7),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/255.255.255.255,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:never, actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2741,8 +2698,8 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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 netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,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/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,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/0xff), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2759,8 +2716,8 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=
 1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,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/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,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/0xff), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2776,8 +2733,8 @@ AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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 netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,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/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,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/0xff), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2817,8 +2774,8 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x8100),vlan(vid=11,pcp=7),encap(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 netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
-skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(1),eth_type(0x8100),vlan(vid=11/0xfff,pcp=7/0x0,cfi=1/1),encap(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/0xff)), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(1),eth_type(0x8100),vlan(vid=11/0xfff,pcp=7/0x0,cfi=1/1),encap(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/0xff)), packets:0, bytes:0, used:never, actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2835,8 +2792,8 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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 netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
-skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2851,8 +2808,8 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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 netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
-skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2905,8 +2862,8 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),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:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0x1,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),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=0xfd/0x3,ttl=128/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(3),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=0xfd/0xff,ttl=128/0xff,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(3),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0x1/0xff,ttl=64/0xff,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(3),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=0xfd/0xff,ttl=128/0xff,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(3),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0x1/0xff,ttl=64/0xff,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2914,6 +2871,7 @@ AT_CLEANUP
 AT_SETUP([ofproto-dpif megaflow - dec_ttl])
 OVS_VSWITCHD_START
 ADD_OF_PORTS([br0], [1], [2])
+AT_CHECK([ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- --id=@N1 create Flow_Table name=t0 prefixes=nw_dst,nw_src], [0], [ignore], [])
 AT_DATA([flows.txt], [dnl
 table=0 in_port=1,icmp,nw_src=10.0.0.4 actions=dec_ttl,output(2)
 ])
@@ -2921,8 +2879,8 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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 netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
-skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/0.0.0.0,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
-skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.252,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no), packets:0, bytes:0, used:never, actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2947,8 +2905,30 @@ dnl MAC, so both the source and destination MAC addresses are
 dnl un-wildcarded, since the ODP commit functions update both the source
 dnl and destination MAC addresses.
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_USED], [0], [dnl
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/ff:ff:ff:ff:ff:ff),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/0xff), packets:0, bytes:0, used:0.0s, actions:2
-skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions:set(eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0a)),2
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/ff:ff:ff:ff:ff:ff),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/0xff), packets:0, bytes:0, used:never, actions:2
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions:set(eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0a)),2
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif megaflow - disabled])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1,ip,nw_dst=10.0.0.1 actions=output(2)
+table=0 in_port=1,ip,nw_dst=10.0.0.3 actions=drop
+])
+AT_CHECK([ovs-appctl upcall/disable-megaflows], [0], [megaflows disabled
+], [])
+AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg], [0], [], [])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+for i in 1 2 3 4; do
+    AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),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 netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+done
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_USED], [0], [dnl
+skb_priority(0),skb_mark(0),in_port(1/0xffff),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/255.255.255.255,proto=1/0xff,tos=0/0xff,ttl=64/0xff,frag=no/0xfc),icmp(type=8,code=0), packets:3, bytes:180, used:0.0s, actions:2
+skb_priority(0),skb_mark(0),in_port(1/0xffff),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/255.255.255.255,proto=1/0xff,tos=0/0xff,ttl=64/0xff,frag=no/0xfc),icmp(type=8,code=0), packets:3, bytes:180, used:0.0s, actions:drop
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -3023,11 +3003,9 @@ AT_CHECK([ovs-appctl bfd/show | sed -n '/^.*Session State:.*/p'], [0], [dnl
        Remote Session State: down
 ])
 # bond/show should show 'may-enable: false' for p0.
-AT_CHECK([ovs-appctl bond/show | sed -n '/^.*may_enable:.*/p'], [0], [dnl
+AT_CHECK([ovs-appctl bond/show br0bond | sed -n '/^.*may_enable:.*/p'], [0], [dnl
        may_enable: false
        may_enable: true
-       may_enable: true
-       may_enable: true
 ])
 
 # now enable the bfd on p1 and disable bfd on p0.
index be7387d..f6a62cd 100644 (file)
@@ -6,6 +6,29 @@ AT_CHECK([ovs-ofctl -vwarn probe br0])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto - handling messages with bad version])
+OVS_VSWITCHD_START
+
+# Start a monitor running OpenFlow 1.0, then send the switch an OF1.1 features
+# request
+AT_CHECK([ovs-ofctl -P openflow10 monitor br0 --detach --no-chdir --pidfile])
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+AT_CAPTURE_FILE([monitor.log])
+ovs-appctl -t ovs-ofctl ofctl/send 0205000801234567
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//
+/ECHO/d' monitor.log], [0], [dnl
+send: OFPT_FEATURES_REQUEST (OF1.1):
+OFPT_ERROR (OF1.1): OFPBRC_BAD_VERSION
+OFPT_FEATURES_REQUEST (OF1.1):
+OFPT_BARRIER_REPLY:
+])
+
+OVS_VSWITCHD_STOP(["/received OpenFlow version 0x02 != expected 01/d"])
+AT_CLEANUP
+
 AT_SETUP([ofproto - feature request, config request])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl -vwarn show br0], [0], [stdout])
@@ -1658,31 +1681,34 @@ AT_CHECK([ovs-ofctl -O OpenFlow12 monitor br0 --detach --no-chdir --pidfile])
 ovs-appctl -t ovs-ofctl ofctl/barrier
 ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
 : > expout
+: > experr
 
 # find out current role
 ovs-appctl -t ovs-ofctl ofctl/send 031800180000000200000000000000000000000000000000
-echo >>expout "send: OFPT_ROLE_REQUEST (OF1.2) (xid=0x2): role=nochange"
+echo >>experr "send: OFPT_ROLE_REQUEST (OF1.2) (xid=0x2): role=nochange"
 echo >>expout "OFPT_ROLE_REPLY (OF1.2) (xid=0x2): role=equal"
 
 # Become slave (generation_id is initially undefined, so 2^63+2 should not be stale)
 ovs-appctl -t ovs-ofctl ofctl/send 031800180000000300000003000000008000000000000002
-echo >>expout "send: OFPT_ROLE_REQUEST (OF1.2) (xid=0x3): role=slave generation_id=9223372036854775810"
+echo >>experr "send: OFPT_ROLE_REQUEST (OF1.2) (xid=0x3): role=slave generation_id=9223372036854775810"
 echo >>expout "OFPT_ROLE_REPLY (OF1.2) (xid=0x3): role=slave generation_id=9223372036854775810"
 
 # Try to become the master using a stale generation ID
 ovs-appctl -t ovs-ofctl ofctl/send 031800180000000400000002000000000000000000000002
-echo >>expout "send: OFPT_ROLE_REQUEST (OF1.2) (xid=0x4): role=master generation_id=2"
+echo >>experr "send: OFPT_ROLE_REQUEST (OF1.2) (xid=0x4): role=master generation_id=2"
 echo >>expout "OFPT_ERROR (OF1.2) (xid=0x4): OFPRRFC_STALE"
 echo >>expout "OFPT_ROLE_REQUEST (OF1.2) (xid=0x4): role=master generation_id=2"
 
 # Become master using a valid generation ID
 ovs-appctl -t ovs-ofctl ofctl/send 031800180000000500000002000000000000000000000001
-echo >>expout "send: OFPT_ROLE_REQUEST (OF1.2) (xid=0x5): role=master generation_id=1"
+echo >>experr "send: OFPT_ROLE_REQUEST (OF1.2) (xid=0x5): role=master generation_id=1"
 echo >>expout "OFPT_ROLE_REPLY (OF1.2) (xid=0x5): role=master generation_id=1"
 ovs-appctl -t ovs-ofctl ofctl/barrier
 echo >>expout "OFPT_BARRIER_REPLY (OF1.2) (xid=0x3):"
 
-AT_CHECK([cat monitor.log], [0], [expout])
+AT_CHECK([grep -v '^send:' monitor.log], [0], [expout])
+mv experr expout
+AT_CHECK([grep '^send:' monitor.log], [0], [expout])
 
 ovs-appctl -t ovs-ofctl exit
 OVS_VSWITCHD_STOP
@@ -2152,3 +2178,58 @@ OFPT_BARRIER_REPLY (OF1.3):
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([ofproto - ofport_request])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2], [3])
+
+set_and_check_specific_ofports () {
+    ovs-vsctl set Interface p1 ofport_request="$1" -- \
+             set Interface p2 ofport_request="$2" -- \
+             set Interface p3 ofport_request="$3"
+    ofports=`ovs-vsctl get Interface p1 ofport -- \
+                      get Interface p2 ofport -- \
+                      get Interface p3 ofport`
+    AT_CHECK_UNQUOTED([echo $ofports], [0], [$1 $2 $3
+])
+}
+for pre in      '1 2 3' '1 3 2' '2 1 3' '2 3 1' '3 1 2' '3 2 1'; do
+    for post in '1 2 3' '1 3 2' '2 1 3' '2 3 1' '3 1 2' '3 2 1'; do
+        echo -----------------------------------------------------------
+        echo "Check changing port numbers from $pre to $post"
+       set_and_check_ofports $pre
+       set_and_check_ofports $post
+    done
+done
+
+ovs-vsctl del-port p3
+
+set_and_check_poorly_specified_ofports () {
+    ovs-vsctl set Interface p1 ofport_request="$1" -- \
+             set Interface p2 ofport_request="$2"
+    p1=`ovs-vsctl get Interface p1 ofport`
+    p2=`ovs-vsctl get Interface p2 ofport`
+    echo $p1 $p2
+
+    AT_CHECK([test "$p1" != "$p2"])
+    if test "$1" = "$2" && test "$1" != '[[]]'; then
+        # One port number must be the requested one.
+       AT_CHECK([test "$p1" = "$1" || test "$p2" = "$1"])
+       # The other port number must be different (already tested above).
+    else
+        AT_CHECK([test "$1" = '[[]]' || test "$p1" == "$1"])
+        AT_CHECK([test "$2" = '[[]]' || test "$p2" == "$2"])
+    fi
+}
+for pre in      '1 2' '[[]] 2' '1 [[]]' '[[]] [[]]' '2 1' '[[]] 1' '2 [[]]' \
+                '1 1' '2 2'; do
+    for post in '1 2' '[[]] 2' '1 [[]]' '[[]] [[]]' '2 1' '[[]] 1' '2 [[]]' \
+                '1 1' '2 2'; do
+        echo -----------------------------------------------------------
+        echo "Check changing port numbers from $pre to $post"
+        set_and_check_poorly_specified_ofports $pre
+        set_and_check_poorly_specified_ofports $post
+    done
+done
+OVS_VSWITCHD_STOP
+AT_CLEANUP
index 6e6461b..bda8666 100644 (file)
@@ -2485,7 +2485,7 @@ AT_CHECK([ovs-ofctl add-flow br0 "tcp,tcp_flags=+ack-ack,action="], [1], [],
   [ovs-ofctl: ack: Each TCP flag can be specified only once
 ])
 
-AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
+AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
                p1 1/1: (dummy)
                p2 2/2: (dummy)
 ])
index 444ab96..a2c53f3 100644 (file)
@@ -38,6 +38,8 @@ cat stdout >> output
 
 EXECUTION_EXAMPLES
 \f
+AT_BANNER([ovsdb-server miscellaneous features])
+
 AT_SETUP([truncating corrupted database log])
 AT_KEYWORDS([ovsdb server positive unix])
 OVS_RUNDIR=`pwd`; export OVS_RUNDIR
@@ -481,7 +483,7 @@ AT_KEYWORDS([ovsdb server positive ssl $5])
 AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
 PKIDIR=$abs_top_builddir/tests
 AT_SKIP_IF([expr "$PKIDIR" : ".*[      '\"
-\r\\]"])
+\\]"])
 AT_DATA([schema],
   [[{"name": "mydb",
      "tables": {
@@ -662,6 +664,89 @@ _uuid                                name  number
 ], [], [test ! -e pid || kill `cat pid`])
 OVSDB_SERVER_SHUTDOWN
 AT_CLEANUP
+
+AT_SETUP([ovsdb-server connection queue limits])
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+ON_EXIT([kill `cat *.pid`])
+
+# The maximum socket receive buffer size is important for this test, which
+# tests behavior when the receive buffer overflows.
+if test -e /proc/sys/net/core/rmem_max; then
+    # Linux
+    rmem_max=`cat /proc/sys/net/core/rmem_max`
+elif rmem_max=`sysctl -n net.inet.tcp.recvbuf_max 2>/dev/null`; then
+    : # FreeBSD
+else
+    # Don't know how to get maximum socket receive buffer on this OS
+    AT_SKIP_IF([:])
+fi
+# Calculate the total amount of data we need to queue: rmem_max in the
+# kernel plus 1024 kB in jsonrpc-server sending userspace (see default
+# backlog_threshold in ovsdb_jsonrpc_session_create() in
+# jsonrpc-server.c).
+queue_size=`expr $rmem_max + 1024 \* 1024`
+echo rmem_max=$rmem_max queue_size=$queue_size
+
+# Each flow update message takes up at least 48 bytes of space in queues
+# and in practice more than that.
+n_msgs=`expr $queue_size / 48`
+echo n_msgs=$n_msgs
+
+# Start an ovsdb-server with the vswitchd schema.
+OVSDB_INIT([db])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --log-file --remote=punix:socket db],
+  [0], [ignore], [ignore])
+
+# Executes a pair of transactions that add a bridge with 100 ports,
+# and then deletes that bridge.  Each of these transactions yields
+# a monitor update about 25 kB in size.
+trigger_big_update () {
+    ovs-vsctl --db=unix:socket --no-wait -- add-br br0 $add
+    ovs-vsctl --db=unix:socket --no-wait -- del-br br0
+}
+add_ports () {
+    for j in `seq 1 100`; do
+        printf " -- add-port br0 p%d" $j
+    done
+}
+add=`add_ports`
+
+AT_CAPTURE_FILE([ovsdb-client.out])
+AT_CAPTURE_FILE([ovsdb-client.err])
+
+# Start an ovsdb-client monitoring all changes to the database,
+# make it block to force the buffers to fill up, and then execute
+# enough transactions that ovsdb-server disconnects it.
+AT_CHECK([ovsdb-client --detach --no-chdir --pidfile monitor unix:socket ALL >ovsdb-client.out 2>ovsdb-client.err])
+AT_CHECK([ovs-appctl -t ovsdb-client ovsdb-client/block])
+for i in `seq 1 100`; do
+    echo "blocked update ($i of 100)"
+    trigger_big_update
+done
+AT_CHECK([ovs-appctl -t ovsdb-client ovsdb-client/unblock])
+
+# Make sure that ovsdb-server disconnected the client and
+# that the client exited as a result.
+if grep "bytes backlogged but a complete replica would only take [[0-9]]* bytes, disconnecting" ovsdb-server.log; then
+    :
+else
+    AT_FAIL_IF([:])
+fi
+OVS_WAIT_WHILE([test -e ovsdb-client.pid])
+
+# Start an ovsdb-client monitoring all changes to the database,
+# without making it block, and then execute the same transactions that
+# we did before.  This time the client should not get disconnected.
+AT_CHECK([ovsdb-client --detach --no-chdir --pidfile monitor unix:socket ALL >ovsdb-client.out 2>ovsdb-client.err])
+for i in `seq 1 100`; do
+    echo "unblocked update ($i of 100)"
+    trigger_big_update
+
+    # Make sure that ovsdb-client gets enough CPU time to process the updates.
+    ovs-appctl -t ovsdb-client version > /dev/null
+done
+AT_CLEANUP
 \f
 AT_BANNER([OVSDB -- ovsdb-server transactions (SSL sockets)])
 
index ee7e76c..93a2dc1 100644 (file)
@@ -191,7 +191,7 @@ tcls_remove(struct tcls *cls, const struct test_rule *rule)
             return;
         }
     }
-    NOT_REACHED();
+    OVS_NOT_REACHED();
 }
 
 static bool
@@ -245,7 +245,7 @@ match(const struct cls_rule *wild_, const struct flow *fixed)
                     ^ wild.flow.in_port.ofp_port)
                    & wild.wc.masks.in_port.ofp_port);
         } else {
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
 
         if (!eq) {
@@ -558,7 +558,7 @@ make_rule(int wc_fields, unsigned int priority, int value_pat)
         } else if (f_idx == CLS_F_IDX_IN_PORT) {
             match.wc.masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX);
         } else {
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
 
@@ -609,6 +609,10 @@ shuffle_u32s(uint32_t *p, size_t n)
 \f
 /* Classifier tests. */
 
+static enum mf_field_id trie_fields[2] = {
+    MFF_IPV4_DST, MFF_IPV4_SRC
+};
+
 /* Tests an empty classifier. */
 static void
 test_empty(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
@@ -617,7 +621,8 @@ test_empty(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     struct tcls tcls;
 
     classifier_init(&cls, flow_segment_u32s);
-    ovs_rwlock_rdlock(&cls.rwlock);
+    ovs_rwlock_wrlock(&cls.rwlock);
+    classifier_set_prefix_fields(&cls, trie_fields, ARRAY_SIZE(trie_fields));
     tcls_init(&tcls);
     assert(classifier_is_empty(&cls));
     assert(tcls_is_empty(&tcls));
@@ -650,6 +655,8 @@ test_single_rule(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 
         classifier_init(&cls, flow_segment_u32s);
         ovs_rwlock_wrlock(&cls.rwlock);
+        classifier_set_prefix_fields(&cls, trie_fields,
+                                     ARRAY_SIZE(trie_fields));
         tcls_init(&tcls);
 
         tcls_rule = tcls_insert(&tcls, rule);
@@ -689,6 +696,8 @@ test_rule_replacement(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 
         classifier_init(&cls, flow_segment_u32s);
         ovs_rwlock_wrlock(&cls.rwlock);
+        classifier_set_prefix_fields(&cls, trie_fields,
+                                     ARRAY_SIZE(trie_fields));
         tcls_init(&tcls);
         tcls_insert(&tcls, rule1);
         classifier_insert(&cls, &rule1->cls_rule);
@@ -801,6 +810,8 @@ test_many_rules_in_one_list (int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 
             classifier_init(&cls, flow_segment_u32s);
             ovs_rwlock_wrlock(&cls.rwlock);
+            classifier_set_prefix_fields(&cls, trie_fields,
+                                         ARRAY_SIZE(trie_fields));
             tcls_init(&tcls);
 
             for (i = 0; i < ARRAY_SIZE(ops); i++) {
@@ -903,6 +914,8 @@ test_many_rules_in_one_table(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 
         classifier_init(&cls, flow_segment_u32s);
         ovs_rwlock_wrlock(&cls.rwlock);
+        classifier_set_prefix_fields(&cls, trie_fields,
+                                     ARRAY_SIZE(trie_fields));
         tcls_init(&tcls);
 
         for (i = 0; i < N_RULES; i++) {
@@ -965,6 +978,8 @@ test_many_rules_in_n_tables(int n_tables)
 
         classifier_init(&cls, flow_segment_u32s);
         ovs_rwlock_wrlock(&cls.rwlock);
+        classifier_set_prefix_fields(&cls, trie_fields,
+                                     ARRAY_SIZE(trie_fields));
         tcls_init(&tcls);
 
         for (i = 0; i < MAX_RULES; i++) {
@@ -1098,7 +1113,7 @@ next_random_flow(struct flow *flow, unsigned int idx)
                 }
             }
         }
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     /* 16 randomly chosen flows with N >= 3 nonzero values. */
index 6528b07..99a9e69 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -61,7 +61,7 @@ main(int argc OVS_UNUSED, char *argv[])
         union flow_in_port in_port_;
         n++;
 
-        retval = pcap_read(pcap, &packet);
+        retval = pcap_read(pcap, &packet, NULL);
         if (retval == EOF) {
             ovs_fatal(0, "unexpected end of file reading pcap file");
         } else if (retval) {
index dd5aaa9..ccb1484 100644 (file)
@@ -221,7 +221,7 @@ test_insert_delete__(struct element *elements,
                 goto found;
             }
         }
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     found:
         heap_remove(&heap, &element->heap_node);
@@ -260,7 +260,7 @@ test_insert_delete_raw__(struct element *elements,
                 goto found;
             }
         }
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     found:
         heap_raw_remove(&heap, &element->heap_node);
index bf879c7..7a89779 100644 (file)
@@ -128,7 +128,7 @@ main(int argc, char *argv[])
             break;
 
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
 
index 020ae48..9d1897f 100644 (file)
@@ -166,7 +166,7 @@ do_run(int argc, char *argv[])
     switch (action) {
     default:
         if (action != 0) {
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
         break;
 
index 9152562..363abb1 100644 (file)
@@ -1013,6 +1013,23 @@ test_ovs_scan(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     ovs_assert(sscanf("0x12-3]xyz", "%[^-a-f]", str));
     ovs_assert(!strcmp(str, "0x12"));
 }
+
+static void
+test_snprintf(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    char s[16];
+
+    ovs_assert(snprintf(s, 4, "abcde") == 5);
+    ovs_assert(!strcmp(s, "abc"));
+
+    ovs_assert(snprintf(s, 5, "abcde") == 5);
+    ovs_assert(!strcmp(s, "abcd"));
+
+    ovs_assert(snprintf(s, 6, "abcde") == 5);
+    ovs_assert(!strcmp(s, "abcde"));
+
+    ovs_assert(snprintf(NULL, 0, "abcde") == 5);
+}
 \f
 static const struct command commands[] = {
     {"ctz", 0, 0, test_ctz},
@@ -1028,6 +1045,7 @@ static const struct command commands[] = {
     {"follow-symlinks", 1, INT_MAX, test_follow_symlinks},
     {"assert", 0, 0, test_assert},
     {"ovs_scan", 0, 0, test_ovs_scan},
+    {"snprintf", 0, 0, test_snprintf},
     {NULL, 0, 0, NULL},
 };
 
index 982d22a..4f22b3f 100644 (file)
@@ -14,7 +14,7 @@ actions=IN_PORT
 
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
-AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
                br0 65534/100: (dummy)
                p1 1/1: (gre: remote_ip=1.1.1.1)
                p2 2/1: (gre: local_ip=2.2.2.2, remote_ip=1.1.1.1)
@@ -37,7 +37,7 @@ dnl reconfigure, local_ip, remote_ip
 AT_CHECK([ovs-vsctl set Interface p2 type=gre options:local_ip=2.2.2.3 \
           options:df_default=false options:ttl=1 options:csum=true \
           -- set Interface p3 type=gre64])
-AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
                br0 65534/100: (dummy)
                p1 1/1: (gre: remote_ip=1.1.1.1)
                p2 2/1: (gre: csum=true, df_default=false, local_ip=2.2.2.3, remote_ip=1.1.1.1, ttl=1)
@@ -72,7 +72,7 @@ actions=2
 
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
-AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
                br0 65534/100: (dummy)
                p1 1/1: (gre: remote_ip=1.1.1.1)
                p2 2/2: (dummy)
@@ -116,7 +116,7 @@ actions=output:1
 
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
-AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
                br0 65534/100: (dummy)
                p1 1/1: (gre: key=5, local_ip=2.2.2.2, remote_ip=1.1.1.1)
                p2 2/2: (dummy)
@@ -148,7 +148,7 @@ actions=output:1
 
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
-AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
                br0 65534/100: (dummy)
                p1 1/1: (gre: remote_ip=1.1.1.1, tos=inherit, ttl=inherit)
                p2 2/2: (dummy)
@@ -190,7 +190,7 @@ actions=set_tunnel:1,output:1,set_tunnel:2,output:2,set_tunnel:3,output:3,set_tu
 
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
-AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
                br0 65534/100: (dummy)
                p1 1/1: (gre: key=flow, remote_ip=1.1.1.1)
                p2 2/1: (gre: key=flow, remote_ip=2.2.2.2)
@@ -222,7 +222,7 @@ actions=IN_PORT,output:1,output:2,output:3
 
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
-AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
                br0 65534/100: (dummy)
                p1 1/1: (gre: key=1, remote_ip=1.1.1.1)
                p2 2/1: (gre: in_key=2, out_key=3, remote_ip=1.1.1.1)
@@ -274,7 +274,7 @@ tun_id=4,actions=output:5
 
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
-AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
                br0 65534/100: (dummy)
                p1 1/1: (gre: key=flow, remote_ip=1.1.1.1)
                p2 2/1: (gre: key=3, remote_ip=3.3.3.3)
@@ -310,7 +310,7 @@ AT_SETUP([tunnel - VXLAN])
 OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \
                     options:remote_ip=1.1.1.1 ofport_request=1])
 
-AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
                br0 65534/100: (dummy)
                p1 1/1: (vxlan: remote_ip=1.1.1.1)
 ])
@@ -322,7 +322,7 @@ AT_SETUP([tunnel - LISP])
 OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=lisp \
                     options:remote_ip=1.1.1.1 ofport_request=1])
 
-AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
                br0 65534/100: (dummy)
                p1 1/1: (lisp: remote_ip=1.1.1.1)
 ])
@@ -334,7 +334,7 @@ AT_SETUP([tunnel - different VXLAN UDP port])
 OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \
                     options:remote_ip=1.1.1.1 ofport_request=1 options:dst_port=4341])
 
-AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
                br0 65534/100: (dummy)
                p1 1/1: (vxlan: dst_port=4341, remote_ip=1.1.1.1)
 ])
@@ -343,7 +343,7 @@ dnl change UDP port
 
 AT_CHECK([ovs-vsctl -- set Interface p1 options:dst_port=5000])
 
-AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
                br0 65534/100: (dummy)
                p1 1/2: (vxlan: dst_port=5000, remote_ip=1.1.1.1)
 ])
@@ -352,7 +352,7 @@ dnl change UDP port to default
 
 AT_CHECK([ovs-vsctl -- set Interface p1 options:dst_port=4789])
 
-AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
                br0 65534/100: (dummy)
                p1 1/1: (vxlan: remote_ip=1.1.1.1)
 ])
index b1ac9a5..bba4e90 100644 (file)
  
  LIBNETDISSECT_SRC=print-isakmp.c
  LIBNETDISSECT_OBJ=$(LIBNETDISSECT_SRC:.c=.o)
+@@ -363,7 +364,7 @@ all: $(PROG)
+ $(PROG): $(OBJ) 
+       @rm -f $@
+-      $(CC) $(FULL_CFLAGS) $(LDFLAGS) -o $@ $(OBJ) $(LIBS)
++      libtool --mode=link $(CC) $(FULL_CFLAGS) $(LDFLAGS) -o $@ $(OBJ) $(LIBS)
+ $(LIBNETDISSECT): $(LIBNETDISSECT_OBJ)
+       @rm -f $@
 --- tcpdump/print-openflow.c   1969-12-31 16:00:00.000000000 -0800
 +++ tcpdump/print-openflow.c   2009-05-11 15:38:41.000000000 -0700
 @@ -0,0 +1,45 @@
index b7a4d73..ffc48b1 100644 (file)
@@ -97,32 +97,32 @@ man_MANS += \
 dist_man_MANS += utilities/ovs-ctl.8
 
 utilities_ovs_appctl_SOURCES = utilities/ovs-appctl.c
-utilities_ovs_appctl_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+utilities_ovs_appctl_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 utilities_ovs_dpctl_SOURCES = utilities/ovs-dpctl.c
-utilities_ovs_dpctl_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+utilities_ovs_dpctl_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 utilities_ovs_ofctl_SOURCES = utilities/ovs-ofctl.c
 utilities_ovs_ofctl_LDADD = \
-       ofproto/libofproto.a \
-       lib/libopenvswitch.a \
+       ofproto/libofproto.la \
+       lib/libopenvswitch.la \
        $(SSL_LIBS)
 
 utilities_ovs_vsctl_SOURCES = utilities/ovs-vsctl.c
-utilities_ovs_vsctl_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+utilities_ovs_vsctl_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 if LINUX_DATAPATH
 sbin_PROGRAMS += utilities/ovs-vlan-bug-workaround
 utilities_ovs_vlan_bug_workaround_SOURCES = utilities/ovs-vlan-bug-workaround.c
-utilities_ovs_vlan_bug_workaround_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+utilities_ovs_vlan_bug_workaround_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 noinst_PROGRAMS += utilities/nlmon
 utilities_nlmon_SOURCES = utilities/nlmon.c
-utilities_nlmon_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+utilities_nlmon_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 endif
 
 bin_PROGRAMS += utilities/ovs-benchmark
 utilities_ovs_benchmark_SOURCES = utilities/ovs-benchmark.c
-utilities_ovs_benchmark_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+utilities_ovs_benchmark_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 include utilities/bugtool/automake.mk
index ddeeb1d..9fd0234 100644 (file)
@@ -69,7 +69,7 @@ main(int argc, char *argv[])
     } else if (cmd_result) {
         fputs(cmd_result, stdout);
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 
     jsonrpc_close(client);
@@ -161,7 +161,7 @@ parse_command_line(int argc, char *argv[])
             exit(EXIT_FAILURE);
 
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
 
index 9b806ed..ac54f6c 100755 (executable)
@@ -98,4 +98,5 @@ if bad_pids:
     print """
 The following processes are listening for packets to arrive on network devices
 that no longer exist. You may want to restart them."""
+    sys.stdout.flush()
     os.execvp("ps", ["ps"] + ["%s" % pid for pid in bad_pids])
index d33b2ba..7b69094 100755 (executable)
@@ -24,7 +24,9 @@ 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}
+BUILD_GCC = OVS_SRC + "/_build-gcc"
+BUILD_CLANG = OVS_SRC + "/_build-clang"
+PATH = "%(ovs)s/utilities:%(ovs)s/ovsdb:%(ovs)s/vswitchd" % {"ovs": BUILD_GCC}
 
 ENV["CFLAGS"] = "-g -O0"
 ENV["PATH"] = PATH + ":" + ENV["PATH"]
@@ -52,10 +54,13 @@ def uname():
 
 def conf():
     tag()
-    if options.clang:
-        ENV["CC"] = "clang"
 
-    configure = ["./configure", "--prefix=" + ROOT, "--localstatedir=" + ROOT,
+    try:
+        os.remove(OVS_SRC + "/Makefile")
+    except OSError:
+        pass
+
+    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]
@@ -70,21 +75,59 @@ def conf():
         configure.append("--mandir=" + options.mandir)
 
     _sh("./boot.sh")
+
+    try:
+        os.mkdir(BUILD_GCC)
+    except OSError:
+        pass # Directory exists.
+
+    os.chdir(BUILD_GCC)
     _sh(*configure)
+
+    try:
+        _sh("clang --version", check=True)
+        clang = True
+    except subprocess.CalledProcessError:
+        clang = False
+
+    try:
+        _sh("sparse --version", check=True)
+        sparse = True
+    except subprocess.CalledProcessError:
+        sparse = False
+
+    if clang:
+        try:
+            os.mkdir(BUILD_CLANG)
+        except OSError:
+            pass # Directory exists.
+
+        ENV["CC"] = "clang"
+        os.chdir(BUILD_CLANG)
+        _sh(*configure)
+
+    if sparse:
+        c1 = "C=1"
+    else:
+        c1 = ""
+
+    os.chdir(OVS_SRC)
+
+    make_str = "\t$(MAKE) -C %s $@\n"
+
+    mf = open(OVS_SRC + "/Makefile", "w")
+    mf.write("all:\n%:\n")
+    if clang:
+        mf.write(make_str % BUILD_CLANG)
+    mf.write("\t$(MAKE) -C %s %s $@\n" % (BUILD_GCC, c1))
+    mf.write("\ncheck:\n")
+    mf.write(make_str % BUILD_GCC)
+    mf.close()
 commands.append(conf)
 
 
 def make(args=""):
     make = "make -s -j 8 " + args
-    try:
-        _sh("cgcc", "--version", capture=True)
-        # XXX: For some reason the clang build doesn't place nicely with
-        # sparse.  At some point this needs to be figured out and this check
-        # removed.
-        if not options.clang:
-            make += " C=1"
-    except OSError:
-        pass
     _sh(make)
 commands.append(make)
 
@@ -169,11 +212,11 @@ def run():
     _sh("ovs-vsctl --no-wait set Open_vSwitch %s ovs_version=%s"
         % (root_uuid, version))
 
-    cmd = [OVS_SRC + "/vswitchd/ovs-vswitchd"]
+    cmd = [BUILD_GCC + "/vswitchd/ovs-vswitchd"]
     if options.gdb:
         cmd = ["gdb", "--args"] + cmd
     elif options.valgrind:
-        cmd = ["valgrind", "--track-origins=yes",
+        cmd = ["valgrind", "--track-origins=yes", "--leak-check=full",
                "--suppressions=%s/tests/glibc.supp" % OVS_SRC,
                "--suppressions=%s/tests/openssl.supp" % OVS_SRC] + cmd
     else:
@@ -284,8 +327,6 @@ def main():
                      help="run ovs-vswitchd under gdb")
     group.add_option("--valgrind", dest="valgrind", action="store_true",
                      help="run ovs-vswitchd under valgrind")
-    group.add_option("--clang", dest="clang", action="store_true",
-                     help="build ovs-vswitchd with clang")
     parser.add_option_group(group)
 
     options, args = parser.parse_args()
index 29f1151..09db084 100644 (file)
@@ -350,7 +350,6 @@ dpctl_add_if(int argc OVS_UNUSED, char *argv[])
 
         error = netdev_set_config(netdev, &args);
         if (error) {
-            ovs_error(error, "%s: failed to configure network device", name);
             goto next;
         }
 
@@ -459,7 +458,6 @@ dpctl_set_if(int argc, char *argv[])
         error = netdev_set_config(netdev, &args);
         smap_destroy(&args);
         if (error) {
-            ovs_error(error, "%s: failed to configure network device", name);
             goto next;
         }
 
@@ -563,11 +561,11 @@ show_dpif(struct dpif *dpif)
         printf("\tlookups: hit:%"PRIu64" missed:%"PRIu64" lost:%"PRIu64"\n"
                "\tflows: %"PRIu64"\n",
                stats.n_hit, stats.n_missed, stats.n_lost, stats.n_flows);
-        if (stats.n_masks != UINT64_MAX) {
+        if (stats.n_masks != UINT32_MAX) {
             uint64_t n_pkts = stats.n_hit + stats.n_missed;
             double avg = n_pkts ? (double) stats.n_mask_hit / n_pkts : 0.0;
 
-            printf("\tmasks: hit:%"PRIu64" total:%"PRIu64" hit/pkt:%.2f\n",
+            printf("\tmasks: hit:%"PRIu64" total:%"PRIu32" hit/pkt:%.2f\n",
                    stats.n_mask_hit, stats.n_masks, avg);
         }
     }
index 6f2382b..9a8fd33 100644 (file)
@@ -487,6 +487,21 @@ series of OpenFlow messages in the binary format used on an OpenFlow
 connection, and prints them to the console.  This can be useful for
 printing OpenFlow messages captured from a TCP stream.
 .
+.IP "\fBofp\-parse\-pcap\fR \fIfile\fR [\fIport\fR...]"
+Reads \fIfile\fR, which must be in the PCAP format used by network
+capture tools such as \fBtcpdump\fR or \fBwireshark\fR, extracts all
+the TCP streams for OpenFlow connections, and prints the OpenFlow
+messages in those connections in human-readable format on
+\fBstdout\fR.
+.IP
+OpenFlow connections are distinguished by TCP port number.
+Non-OpenFlow packets are ignored.  By default, data on TCP ports 6633
+and 6653 are considered to be OpenFlow.  Specify one or more
+\fIport\fR arguments to override the default.
+.IP
+This command cannot usefully print SSL encrypted traffic.  It does not
+understand IPv6.
+.
 .SS "Flow Syntax"
 .PP
 Some \fBovs\-ofctl\fR commands accept an argument that describes a flow or
@@ -1195,14 +1210,14 @@ allows isn't supported at the moment.)
 A priority of zero and the tag of zero are used for the new tag.
 .
 .IP \fBpush_mpls\fR:\fIethertype\fR
-If the packet does not already contain any MPLS labels, changes the
-packet's Ethertype to \fIethertype\fR, which must be either the MPLS
-unicast Ethertype \fB0x8847\fR or the MPLS multicast Ethertype
-\fB0x8848\fR, and then pushes an initial label stack entry.  The label
-stack entry's default label is 2 if the packet contains IPv6 and 0
-otherwise, its default traffic control value is the low 3 bits of the
-packet's DSCP value (0 if the packet is not IP), and its TTL is copied
-from the IP TTL (64 if the packet is not IP).
+Changes the packet's Ethertype to \fIethertype\fR, which must be either
+\fB0x8847\fR or \fB0x8848\fR, and pushes an MPLS LSE.
+.IP
+If the packet does not already contain any MPLS labels then an initial
+label stack entry is pushed.  The label stack entry's label is 2 if the
+packet contains IPv6 and 0 otherwise, its default traffic control value is
+the low 3 bits of the packet's DSCP value (0 if the packet is not IP), and
+its TTL is copied from the IP TTL (64 if the packet is not IP).
 .IP
 If the packet does already contain an MPLS label, pushes a new
 outermost label as a copy of the existing outermost label.
@@ -2011,7 +2026,8 @@ affects the \fBmonitor\fR command.
 .
 .IP "\fB\-\-timestamp\fR"
 Print a timestamp before each received packet.  This option only
-affects the \fBmonitor\fR and \fBsnoop\fR commands.
+affects the \fBmonitor\fR, \fBsnoop\fR, and \fBofp\-parse\-pcap\fR
+commands.
 .
 .IP "\fB\-m\fR"
 .IQ "\fB\-\-more\fR"
index 5eb8cf4..9b02b25 100644 (file)
@@ -325,7 +325,8 @@ usage(void)
            "  benchmark TARGET N COUNT    bandwidth of COUNT N-byte echos\n"
            "SWITCH or TARGET is an active OpenFlow connection method.\n"
            "\nOther commands:\n"
-           "  ofp-parse FILE              print messages read from FILE\n",
+           "  ofp-parse FILE              print messages read from FILE\n"
+           "  ofp-parse-pcap PCAP         print OpenFlow read from PCAP\n",
            program_name, program_name);
     vconn_usage(true, false, false);
     daemon_usage();
@@ -1539,7 +1540,7 @@ ofctl_monitor(int argc, char *argv[])
         case OFP13_VERSION:
             break;
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
 
@@ -1601,7 +1602,7 @@ ofctl_packet_out(int argc, char *argv[])
     struct vconn *vconn;
     char *error;
     int i;
-    enum ofputil_protocol usable_protocols; /* TODO: Use in proto selection */
+    enum ofputil_protocol usable_protocols; /* XXX: Use in proto selection */
 
     ofpbuf_init(&ofpacts, 64);
     error = parse_ofpacts(argv[3], &ofpacts, &usable_protocols);
@@ -1825,6 +1826,92 @@ ofctl_ofp_parse(int argc OVS_UNUSED, char *argv[])
     }
 }
 
+static bool
+is_openflow_port(ovs_be16 port_, char *ports[])
+{
+    uint16_t port = ntohs(port_);
+    if (ports[0]) {
+        int i;
+
+        for (i = 0; ports[i]; i++) {
+            if (port == atoi(ports[i])) {
+                return true;
+            }
+        }
+        return false;
+    } else {
+        return port == OFP_PORT || port == OFP_OLD_PORT;
+    }
+}
+
+static void
+ofctl_ofp_parse_pcap(int argc OVS_UNUSED, char *argv[])
+{
+    struct tcp_reader *reader;
+    FILE *file;
+    int error;
+    bool first;
+
+    file = pcap_open(argv[1], "rb");
+    if (!file) {
+        ovs_fatal(errno, "%s: open failed", argv[1]);
+    }
+
+    reader = tcp_reader_open();
+    first = true;
+    for (;;) {
+        struct ofpbuf *packet;
+        long long int when;
+        struct flow flow;
+
+        error = pcap_read(file, &packet, &when);
+        if (error) {
+            break;
+        }
+        flow_extract(packet, 0, 0, NULL, NULL, &flow);
+        if (flow.dl_type == htons(ETH_TYPE_IP)
+            && flow.nw_proto == IPPROTO_TCP
+            && (is_openflow_port(flow.tp_src, argv + 2) ||
+                is_openflow_port(flow.tp_dst, argv + 2))) {
+            struct ofpbuf *payload = tcp_reader_run(reader, &flow, packet);
+            if (payload) {
+                while (payload->size >= sizeof(struct ofp_header)) {
+                    const struct ofp_header *oh;
+                    int length;
+
+                    /* Align OpenFlow on 8-byte boundary for safe access. */
+                    ofpbuf_shift(payload, -((intptr_t) payload->data & 7));
+
+                    oh = payload->data;
+                    length = ntohs(oh->length);
+                    if (payload->size < length) {
+                        break;
+                    }
+
+                    if (!first) {
+                        putchar('\n');
+                    }
+                    first = false;
+
+                    if (timestamp) {
+                        char *s = xastrftime_msec("%H:%M:%S.### ", when, true);
+                        fputs(s, stdout);
+                        free(s);
+                    }
+
+                    printf(IP_FMT".%"PRIu16" > "IP_FMT".%"PRIu16":\n",
+                           IP_ARGS(flow.nw_src), ntohs(flow.tp_src),
+                           IP_ARGS(flow.nw_dst), ntohs(flow.tp_dst));
+                    ofp_print(stdout, payload->data, length, verbosity + 1);
+                    ofpbuf_pull(payload, length);
+                }
+            }
+        }
+        ofpbuf_delete(packet);
+    }
+    tcp_reader_close(reader);
+}
+
 static void
 ofctl_ping(int argc, char *argv[])
 {
@@ -3122,7 +3209,7 @@ ofctl_parse_pcap(int argc OVS_UNUSED, char *argv[])
         struct flow flow;
         int error;
 
-        error = pcap_read(pcap, &packet);
+        error = pcap_read(pcap, &packet, NULL);
         if (error == EOF) {
             break;
         } else if (error) {
@@ -3365,11 +3452,13 @@ static const struct command all_commands[] = {
     { "mod-table", 3, 3, ofctl_mod_table },
     { "get-frags", 1, 1, ofctl_get_frags },
     { "set-frags", 2, 2, ofctl_set_frags },
-    { "ofp-parse", 1, 1, ofctl_ofp_parse },
     { "probe", 1, 1, ofctl_probe },
     { "ping", 1, 2, ofctl_ping },
     { "benchmark", 3, 3, ofctl_benchmark },
 
+    { "ofp-parse", 1, 1, ofctl_ofp_parse },
+    { "ofp-parse-pcap", 1, INT_MAX, ofctl_ofp_parse_pcap },
+
     { "add-group", 1, 2, ofctl_add_group },
     { "add-groups", 1, 2, ofctl_add_groups },
     { "mod-group", 1, 2, ofctl_mod_group },
index 8688f61..88fb2b1 100644 (file)
@@ -138,7 +138,7 @@ parse_options(int argc, char *argv[])
             exit(EXIT_FAILURE);
 
         default:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
     free(short_options);
index 0527885..528b40c 100644 (file)
@@ -3630,7 +3630,7 @@ post_create(struct vsctl_context *ctx)
     struct uuid dummy;
 
     if (!uuid_from_string(&dummy, ds_cstr(&ctx->output))) {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
     real = ovsdb_idl_txn_get_insert_uuid(ctx->txn, &dummy);
     if (real) {
@@ -3748,7 +3748,7 @@ evaluate_relop(const struct ovsdb_datum *a, const struct ovsdb_datum *b,
         return ovsdb_datum_includes_all(b, a, type);
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -4058,7 +4058,7 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands,
     switch (status) {
     case TXN_UNCOMMITTED:
     case TXN_INCOMPLETE:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case TXN_ABORTED:
         /* Should not happen--we never call ovsdb_idl_txn_abort(). */
@@ -4079,7 +4079,7 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands,
         vsctl_fatal("database not locked");
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
     free(error);
 
index 02d413e..b0a386b 100644 (file)
@@ -12,9 +12,9 @@ vswitchd_ovs_vswitchd_SOURCES = \
        vswitchd/xenserver.c \
        vswitchd/xenserver.h
 vswitchd_ovs_vswitchd_LDADD = \
-       ofproto/libofproto.a \
-       lib/libsflow.a \
-       lib/libopenvswitch.a \
+       ofproto/libofproto.la \
+       lib/libsflow.la \
+       lib/libopenvswitch.la \
        $(SSL_LIBS)
 EXTRA_DIST += vswitchd/INTERNALS
 MAN_ROOTS += vswitchd/ovs-vswitchd.8.in
index c2307be..746097f 100644 (file)
@@ -22,6 +22,7 @@
 #include "bfd.h"
 #include "bitmap.h"
 #include "cfm.h"
+#include "connectivity.h"
 #include "coverage.h"
 #include "daemon.h"
 #include "dirs.h"
@@ -41,6 +42,7 @@
 #include "ofproto/bond.h"
 #include "ofproto/ofproto.h"
 #include "poll-loop.h"
+#include "seq.h"
 #include "sha1.h"
 #include "shash.h"
 #include "smap.h"
@@ -63,33 +65,20 @@ VLOG_DEFINE_THIS_MODULE(bridge);
 
 COVERAGE_DEFINE(bridge_reconfigure);
 
-/* Configuration of an uninstantiated iface. */
-struct if_cfg {
-    struct hmap_node hmap_node;         /* Node in bridge's if_cfg_todo. */
-    const struct ovsrec_interface *cfg; /* Interface record. */
-    const struct ovsrec_port *parent;   /* Parent port record. */
-    ofp_port_t ofport;                  /* Requested OpenFlow port number. */
-};
-
-/* OpenFlow port slated for removal from ofproto. */
-struct ofpp_garbage {
-    struct list list_node;      /* Node in bridge's ofpp_garbage. */
-    ofp_port_t ofp_port;        /* Port to be deleted. */
-};
-
 struct iface {
-    /* These members are always valid. */
+    /* These members are always valid.
+     *
+     * They are immutable: they never change between iface_create() and
+     * iface_destroy(). */
     struct list port_elem;      /* Element in struct port's "ifaces" list. */
     struct hmap_node name_node; /* In struct bridge's "iface_by_name" hmap. */
+    struct hmap_node ofp_port_node; /* In struct bridge's "ifaces" hmap. */
     struct port *port;          /* Containing port. */
     char *name;                 /* Host network device name. */
-
-    /* These members are valid only after bridge_reconfigure() causes them to
-     * be initialized. */
-    struct hmap_node ofp_port_node; /* In struct bridge's "ifaces" hmap. */
-    ofp_port_t ofp_port;        /* OpenFlow port number, */
-                                /* OFPP_NONE if unknown. */
     struct netdev *netdev;      /* Network device. */
+    ofp_port_t ofp_port;        /* OpenFlow port number. */
+
+    /* These members are valid only within bridge_reconfigure(). */
     const char *type;           /* Usually same as cfg->type. */
     const struct ovsrec_interface *cfg;
 };
@@ -130,13 +119,12 @@ struct bridge {
     struct hmap ifaces;         /* "struct iface"s indexed by ofp_port. */
     struct hmap iface_by_name;  /* "struct iface"s indexed by name. */
 
-    struct list ofpp_garbage;   /* "struct ofpp_garbage" slated for removal. */
-    struct hmap if_cfg_todo;    /* "struct if_cfg"s slated for creation.
-                                   Indexed on 'cfg->name'. */
-
     /* Port mirroring. */
     struct hmap mirrors;        /* "struct mirror" indexed by UUID. */
 
+    /* Used during reconfiguration. */
+    struct shash wanted_ports;
+
     /* Synthetic local port if necessary. */
     struct ovsrec_port synth_local_port;
     struct ovsrec_interface synth_local_iface;
@@ -169,6 +157,9 @@ static struct ovsdb_idl_txn *daemonize_txn;
 /* Most recently processed IDL sequence number. */
 static unsigned int idl_seqno;
 
+/* Track changes to port connectivity. */
+static uint64_t connectivity_seqno = LLONG_MIN;
+
 /* Each time this timer expires, the bridge fetches interface and mirror
  * statistics and pushes them into the database. */
 #define IFACE_STATS_INTERVAL (5 * 1000) /* In milliseconds. */
@@ -183,10 +174,9 @@ static long long int iface_stats_timer = LLONG_MIN;
  * This allows the rest of the code to catch up on important things like
  * forwarding packets. */
 #define OFP_PORT_ACTION_WINDOW 10
-static bool reconfiguring = false;
 
 static void add_del_bridges(const struct ovsrec_open_vswitch *);
-static void bridge_update_ofprotos(void);
+static void bridge_run__(void);
 static void bridge_create(const struct ovsrec_bridge *);
 static void bridge_destroy(struct bridge *);
 static struct bridge *bridge_lookup(const char *name);
@@ -194,9 +184,16 @@ static unixctl_cb_func bridge_unixctl_dump_flows;
 static unixctl_cb_func bridge_unixctl_reconnect;
 static size_t bridge_get_controllers(const struct bridge *br,
                                      struct ovsrec_controller ***controllersp);
-static void bridge_add_del_ports(struct bridge *,
-                                 const unsigned long int *splinter_vlans);
-static void bridge_refresh_ofp_port(struct bridge *);
+static void bridge_collect_wanted_ports(struct bridge *,
+                                        const unsigned long *splinter_vlans,
+                                        struct shash *wanted_ports);
+static void bridge_delete_ofprotos(void);
+static void bridge_delete_or_reconfigure_ports(struct bridge *);
+static void bridge_del_ports(struct bridge *,
+                             const struct shash *wanted_ports);
+static void bridge_add_ports(struct bridge *,
+                             const struct shash *wanted_ports);
+
 static void bridge_configure_flow_miss_model(const char *opt);
 static void bridge_configure_datapath_id(struct bridge *);
 static void bridge_configure_netflow(struct bridge *);
@@ -216,9 +213,6 @@ static void bridge_pick_local_hw_addr(struct bridge *,
 static uint64_t bridge_pick_datapath_id(struct bridge *,
                                         const uint8_t bridge_ea[ETH_ADDR_LEN],
                                         struct iface *hw_addr_iface);
-static void bridge_queue_if_cfg(struct bridge *,
-                                const struct ovsrec_interface *,
-                                const struct ovsrec_port *);
 static uint64_t dpid_from_hash(const void *, size_t nbytes);
 static bool bridge_has_bond_fake_iface(const struct bridge *,
                                        const char *name);
@@ -247,8 +241,8 @@ static bool mirror_configure(struct mirror *);
 static void mirror_refresh_stats(struct mirror *);
 
 static void iface_configure_lacp(struct iface *, struct lacp_slave_settings *);
-static bool iface_create(struct bridge *, struct if_cfg *,
-                         ofp_port_t ofp_port);
+static bool iface_create(struct bridge *, const struct ovsrec_interface *,
+                         const struct ovsrec_port *);
 static bool iface_is_internal(const struct ovsrec_interface *iface,
                               const struct ovsrec_bridge *br);
 static const char *iface_get_type(const struct ovsrec_interface *,
@@ -256,7 +250,6 @@ static const char *iface_get_type(const struct ovsrec_interface *,
 static void iface_destroy(struct iface *);
 static struct iface *iface_lookup(const struct bridge *, const char *name);
 static struct iface *iface_find(const char *name);
-static struct if_cfg *if_cfg_lookup(const struct bridge *, const char *name);
 static struct iface *iface_from_ofp_port(const struct bridge *,
                                          ofp_port_t ofp_port);
 static void iface_set_mac(struct iface *);
@@ -268,6 +261,8 @@ static void iface_refresh_cfm_stats(struct iface *);
 static void iface_refresh_stats(struct iface *);
 static void iface_refresh_status(struct iface *);
 static bool iface_is_synthetic(const struct iface *);
+static ofp_port_t iface_get_requested_ofp_port(
+    const struct ovsrec_interface *);
 static ofp_port_t iface_pick_ofport(const struct ovsrec_interface *);
 
 /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
@@ -491,113 +486,79 @@ static void
 bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
 {
     unsigned long int *splinter_vlans;
-    struct bridge *br;
+    struct sockaddr_in *managers;
+    struct bridge *br, *next;
+    int sflow_bridge_number;
+    size_t n_managers;
 
     COVERAGE_INC(bridge_reconfigure);
 
-    ovs_assert(!reconfiguring);
-    reconfiguring = true;
-
-    ofproto_set_flow_eviction_threshold(
-        smap_get_int(&ovs_cfg->other_config, "flow-eviction-threshold",
-                     OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT));
+    ofproto_set_flow_limit(smap_get_int(&ovs_cfg->other_config, "flow-limit",
+                                        OFPROTO_FLOW_LIMIT_DEFAULT));
 
-    ofproto_set_n_handler_threads(
-        smap_get_int(&ovs_cfg->other_config, "n-handler-threads", 0));
+    ofproto_set_threads(
+        smap_get_int(&ovs_cfg->other_config, "n-handler-threads", 0),
+        smap_get_int(&ovs_cfg->other_config, "n-revalidator-threads", 0));
 
     bridge_configure_flow_miss_model(smap_get(&ovs_cfg->other_config,
                                               "force-miss-model"));
 
     /* Destroy "struct bridge"s, "struct port"s, and "struct iface"s according
-     * to 'ovs_cfg' while update the "if_cfg_queue", with only very minimal
-     * configuration otherwise.
+     * to 'ovs_cfg', with only very minimal configuration otherwise.
      *
      * This is mostly an update to bridge data structures. Nothing is pushed
      * down to ofproto or lower layers. */
     add_del_bridges(ovs_cfg);
     splinter_vlans = collect_splinter_vlans(ovs_cfg);
     HMAP_FOR_EACH (br, node, &all_bridges) {
-        bridge_add_del_ports(br, splinter_vlans);
+        bridge_collect_wanted_ports(br, splinter_vlans, &br->wanted_ports);
+        bridge_del_ports(br, &br->wanted_ports);
     }
     free(splinter_vlans);
 
-    /* Delete datapaths that are no longer configured, and create ones which
-     * don't exist but should. */
-    bridge_update_ofprotos();
-
-    /* Make sure each "struct iface" has a correct ofp_port in its ofproto. */
-    HMAP_FOR_EACH (br, node, &all_bridges) {
-        bridge_refresh_ofp_port(br);
-    }
-
-    /* Clear database records for "if_cfg"s which haven't been instantiated. */
+    /* Start pushing configuration changes down to the ofproto layer:
+     *
+     *   - Delete ofprotos that are no longer configured.
+     *
+     *   - Delete ports that are no longer configured.
+     *
+     *   - Reconfigure existing ports to their desired configurations, or
+     *     delete them if not possible.
+     *
+     * We have to do all the deletions before we can do any additions, because
+     * the ports to be added might require resources that will be freed up by
+     * deletions (they might especially overlap in name). */
+    bridge_delete_ofprotos();
     HMAP_FOR_EACH (br, node, &all_bridges) {
-        struct if_cfg *if_cfg;
-
-        HMAP_FOR_EACH (if_cfg, hmap_node, &br->if_cfg_todo) {
-            iface_clear_db_record(if_cfg->cfg);
+        if (br->ofproto) {
+            bridge_delete_or_reconfigure_ports(br);
         }
     }
 
-    reconfigure_system_stats(ovs_cfg);
-}
-
-static bool
-bridge_reconfigure_ofp(void)
-{
-    long long int deadline;
-    struct bridge *br;
-
-    deadline = time_msec() + OFP_PORT_ACTION_WINDOW;
+    /* Finish pushing configuration changes to the ofproto layer:
+     *
+     *     - Create ofprotos that are missing.
+     *
+     *     - Add ports that are missing. */
+    HMAP_FOR_EACH_SAFE (br, next, node, &all_bridges) {
+        if (!br->ofproto) {
+            int error;
 
-    /* The kernel will reject any attempt to add a given port to a datapath if
-     * that port already belongs to a different datapath, so we must do all
-     * port deletions before any port additions. */
-    HMAP_FOR_EACH (br, node, &all_bridges) {
-        struct ofpp_garbage *garbage, *next;
-
-        LIST_FOR_EACH_SAFE (garbage, next, list_node, &br->ofpp_garbage) {
-            /* It's a bit dangerous to call bridge_run_fast() here as ofproto's
-             * internal datastructures may not be consistent.  Eventually, when
-             * port additions and deletions are cheaper, these calls should be
-             * removed. */
-            bridge_run_fast();
-            ofproto_port_del(br->ofproto, garbage->ofp_port);
-            list_remove(&garbage->list_node);
-            free(garbage);
-
-            if (time_msec() >= deadline) {
-                return false;
+            error = ofproto_create(br->name, br->type, &br->ofproto);
+            if (error) {
+                VLOG_ERR("failed to create bridge %s: %s", br->name,
+                         ovs_strerror(error));
+                shash_destroy(&br->wanted_ports);
+                bridge_destroy(br);
             }
-            bridge_run_fast();
         }
     }
-
     HMAP_FOR_EACH (br, node, &all_bridges) {
-        struct if_cfg *if_cfg, *next;
-
-        HMAP_FOR_EACH_SAFE (if_cfg, next, hmap_node, &br->if_cfg_todo) {
-            iface_create(br, if_cfg, OFPP_NONE);
-            if (time_msec() >= deadline) {
-                return false;
-            }
-        }
+        bridge_add_ports(br, &br->wanted_ports);
+        shash_destroy(&br->wanted_ports);
     }
 
-    return true;
-}
-
-static bool
-bridge_reconfigure_continue(const struct ovsrec_open_vswitch *ovs_cfg)
-{
-    struct sockaddr_in *managers;
-    int sflow_bridge_number;
-    size_t n_managers;
-    struct bridge *br;
-    bool done;
-
-    ovs_assert(reconfiguring);
-    done = bridge_reconfigure_ofp();
+    reconfigure_system_stats(ovs_cfg);
 
     /* Complete the configuration. */
     sflow_bridge_number = 0;
@@ -642,15 +603,20 @@ bridge_reconfigure_continue(const struct ovsrec_open_vswitch *ovs_cfg)
     }
     free(managers);
 
-    return done;
+    /* The ofproto-dpif provider does some final reconfiguration in its
+     * ->type_run() function.  We have to call it before notifying the database
+     * client that reconfiguration is complete, otherwise there is a very
+     * narrow race window in which e.g. ofproto/trace will not recognize the
+     * new configuration (sometimes this causes unit test failures). */
+    bridge_run__();
 }
 
 /* Delete ofprotos which aren't configured or have the wrong type.  Create
  * ofprotos which don't exist but need to. */
 static void
-bridge_update_ofprotos(void)
+bridge_delete_ofprotos(void)
 {
-    struct bridge *br, *next;
+    struct bridge *br;
     struct sset names;
     struct sset types;
     const char *type;
@@ -672,46 +638,148 @@ bridge_update_ofprotos(void)
     }
     sset_destroy(&names);
     sset_destroy(&types);
+}
 
-    /* Add ofprotos for bridges which don't have one yet. */
-    HMAP_FOR_EACH_SAFE (br, next, node, &all_bridges) {
-        struct bridge *br2;
-        int error;
+static ofp_port_t *
+add_ofp_port(ofp_port_t port, ofp_port_t *ports, size_t *n, size_t *allocated)
+{
+    if (*n >= *allocated) {
+        ports = x2nrealloc(ports, allocated, sizeof *ports);
+    }
+    ports[(*n)++] = port;
+    return ports;
+}
 
-        if (br->ofproto) {
-            continue;
-        }
+static void
+bridge_delete_or_reconfigure_ports(struct bridge *br)
+{
+    struct ofproto_port ofproto_port;
+    struct ofproto_port_dump dump;
 
-        /* Remove ports from any datapath with the same name as 'br'.  If we
-         * don't do this, creating 'br''s ofproto will fail because a port with
-         * the same name as its local port already exists. */
-        HMAP_FOR_EACH (br2, node, &all_bridges) {
-            struct ofproto_port ofproto_port;
+    /* List of "ofp_port"s to delete.  We make a list instead of deleting them
+     * right away because ofproto implementations aren't necessarily able to
+     * iterate through a changing list of ports in an entirely robust way. */
+    ofp_port_t *del;
+    size_t n, allocated;
+    size_t i;
+
+    del = NULL;
+    n = allocated = 0;
+
+    OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, br->ofproto) {
+        ofp_port_t requested_ofp_port;
+        struct iface *iface;
 
-            if (!br2->ofproto) {
+        iface = iface_lookup(br, ofproto_port.name);
+        if (!iface) {
+            /* No such iface is configured, so we should delete this
+             * ofproto_port.
+             *
+             * As a corner case exception, keep the port if it's a bond fake
+             * interface. */
+            if (bridge_has_bond_fake_iface(br, ofproto_port.name)
+                && !strcmp(ofproto_port.type, "internal")) {
                 continue;
             }
+            goto delete;
+        }
 
-            if (!ofproto_port_query_by_name(br2->ofproto, br->name,
-                                            &ofproto_port)) {
-                error = ofproto_port_del(br2->ofproto, ofproto_port.ofp_port);
-                if (error) {
-                    VLOG_ERR("failed to delete port %s: %s", ofproto_port.name,
-                             ovs_strerror(error));
-                }
-                ofproto_port_destroy(&ofproto_port);
+        if (strcmp(ofproto_port.type, iface->type)
+            || netdev_set_config(iface->netdev, &iface->cfg->options)) {
+            /* The interface is the wrong type or can't be configured.
+             * Delete it. */
+            goto delete;
+        }
+
+        /* If the requested OpenFlow port for 'iface' changed, and it's not
+         * already the correct port, then we might want to temporarily delete
+         * this interface, so we can add it back again with the new OpenFlow
+         * port number. */
+        requested_ofp_port = iface_get_requested_ofp_port(iface->cfg);
+        if (iface->ofp_port != OFPP_LOCAL &&
+            requested_ofp_port != OFPP_NONE &&
+            requested_ofp_port != iface->ofp_port) {
+            ofp_port_t victim_request;
+            struct iface *victim;
+
+            /* Check for an existing OpenFlow port currently occupying
+             * 'iface''s requested port number.  If there isn't one, then
+             * delete this port.  Otherwise we need to consider further. */
+            victim = iface_from_ofp_port(br, requested_ofp_port);
+            if (!victim) {
+                goto delete;
+            }
+
+            /* 'victim' is a port currently using 'iface''s requested port
+             * number.  Unless 'victim' specifically requested that port
+             * number, too, then we can delete both 'iface' and 'victim'
+             * temporarily.  (We'll add both of them back again later with new
+             * OpenFlow port numbers.)
+             *
+             * If 'victim' did request port number 'requested_ofp_port', just
+             * like 'iface', then that's a configuration inconsistency that we
+             * can't resolve.  We might as well let it keep its current port
+             * number. */
+            victim_request = iface_get_requested_ofp_port(victim->cfg);
+            if (victim_request != requested_ofp_port) {
+                del = add_ofp_port(victim->ofp_port, del, &n, &allocated);
+                iface_destroy(victim);
+                goto delete;
             }
         }
 
-        error = ofproto_create(br->name, br->type, &br->ofproto);
-        if (error) {
-            VLOG_ERR("failed to create bridge %s: %s", br->name,
-                     ovs_strerror(error));
-            bridge_destroy(br);
+        /* Keep it. */
+        continue;
+
+    delete:
+        iface_destroy(iface);
+        del = add_ofp_port(ofproto_port.ofp_port, del, &n, &allocated);
+    }
+
+    for (i = 0; i < n; i++) {
+        ofproto_port_del(br->ofproto, del[i]);
+    }
+    free(del);
+}
+
+static void
+bridge_add_ports__(struct bridge *br, const struct shash *wanted_ports,
+                   bool with_requested_port)
+{
+    struct shash_node *port_node;
+
+    SHASH_FOR_EACH (port_node, wanted_ports) {
+        const struct ovsrec_port *port_cfg = port_node->data;
+        size_t i;
+
+        for (i = 0; i < port_cfg->n_interfaces; i++) {
+            const struct ovsrec_interface *iface_cfg = port_cfg->interfaces[i];
+            ofp_port_t requested_ofp_port;
+
+            requested_ofp_port = iface_get_requested_ofp_port(iface_cfg);
+            if ((requested_ofp_port != OFPP_NONE) == with_requested_port) {
+                struct iface *iface = iface_lookup(br, iface_cfg->name);
+
+                if (!iface) {
+                    iface_create(br, iface_cfg, port_cfg);
+                }
+            }
         }
     }
 }
 
+static void
+bridge_add_ports(struct bridge *br, const struct shash *wanted_ports)
+{
+    /* First add interfaces that request a particular port number. */
+    bridge_add_ports__(br, wanted_ports, true);
+
+    /* Then add interfaces that want automatic port number assignment.
+     * We add these afterward to avoid accidentally taking a specifically
+     * requested port number. */
+    bridge_add_ports__(br, wanted_ports, false);
+}
+
 static void
 port_configure(struct port *port)
 {
@@ -816,9 +884,9 @@ bridge_configure_flow_miss_model(const char *opt)
     enum ofproto_flow_miss_model model = OFPROTO_HANDLE_MISS_AUTO;
 
     if (opt) {
-        if (strcmp(opt, "with-facets")) {
+        if (!strcmp(opt, "with-facets")) {
             model = OFPROTO_HANDLE_MISS_WITH_FACETS;
-        } else if (strcmp(opt, "without-facets")) {
+        } else if (!strcmp(opt, "without-facets")) {
             model = OFPROTO_HANDLE_MISS_WITHOUT_FACETS;
         }
     }
@@ -1325,140 +1393,13 @@ add_del_bridges(const struct ovsrec_open_vswitch *cfg)
     shash_destroy(&new_br);
 }
 
-static void
-iface_set_ofp_port(struct iface *iface, ofp_port_t ofp_port)
-{
-    struct bridge *br = iface->port->bridge;
-
-    ovs_assert(iface->ofp_port == OFPP_NONE && ofp_port != OFPP_NONE);
-    iface->ofp_port = ofp_port;
-    hmap_insert(&br->ifaces, &iface->ofp_port_node,
-                hash_ofp_port(ofp_port));
-    iface_set_ofport(iface->cfg, ofp_port);
-}
-
 /* Configures 'netdev' based on the "options" column in 'iface_cfg'.
  * Returns 0 if successful, otherwise a positive errno value. */
 static int
 iface_set_netdev_config(const struct ovsrec_interface *iface_cfg,
                         struct netdev *netdev)
 {
-    int error;
-
-    error = netdev_set_config(netdev, &iface_cfg->options);
-    if (error) {
-        VLOG_WARN("could not configure network device %s (%s)",
-                  iface_cfg->name, ovs_strerror(error));
-    }
-    return error;
-}
-
-/* This function determines whether 'ofproto_port', which is attached to
- * br->ofproto's datapath, is one that we want in 'br'.
- *
- * If it is, it returns true, after creating an iface (if necessary),
- * configuring the iface's netdev according to the iface's options, and setting
- * iface's ofp_port member to 'ofproto_port->ofp_port'.
- *
- * If, on the other hand, 'port' should be removed, it returns false.  The
- * caller should later detach the port from br->ofproto. */
-static bool
-bridge_refresh_one_ofp_port(struct bridge *br,
-                            const struct ofproto_port *ofproto_port)
-{
-    const char *name = ofproto_port->name;
-    const char *type = ofproto_port->type;
-    ofp_port_t ofp_port = ofproto_port->ofp_port;
-
-    struct iface *iface = iface_lookup(br, name);
-    if (iface) {
-        /* Check that the name-to-number mapping is one-to-one. */
-        if (iface->ofp_port != OFPP_NONE) {
-            VLOG_WARN("bridge %s: interface %s reported twice",
-                      br->name, name);
-            return false;
-        } else if (iface_from_ofp_port(br, ofp_port)) {
-            VLOG_WARN("bridge %s: interface %"PRIu16" reported twice",
-                      br->name, ofp_port);
-            return false;
-        }
-
-        /* There's a configured interface named 'name'. */
-        if (strcmp(type, iface->type)
-            || iface_set_netdev_config(iface->cfg, iface->netdev)) {
-            /* It's the wrong type, or it's the right type but can't be
-             * configured as the user requested, so we must destroy it. */
-            return false;
-        } else {
-            /* It's the right type and configured correctly.  Keep it. */
-            iface_set_ofp_port(iface, ofp_port);
-            return true;
-        }
-    } else if (bridge_has_bond_fake_iface(br, name)
-               && !strcmp(type, "internal")) {
-        /* It's a bond fake iface.  Keep it. */
-        return true;
-    } else {
-        /* There's no configured interface named 'name', but there might be an
-         * interface of that name queued to be created.
-         *
-         * If there is, and it has the correct type, then try to configure it
-         * and add it.  If that's successful, we'll keep it.  Otherwise, we'll
-         * delete it and later try to re-add it. */
-        struct if_cfg *if_cfg = if_cfg_lookup(br, name);
-        return (if_cfg
-                && !strcmp(type, iface_get_type(if_cfg->cfg, br->cfg))
-                && iface_create(br, if_cfg, ofp_port));
-    }
-}
-
-/* Update bridges "if_cfg"s, "struct port"s, and "struct iface"s to be
- * consistent with the ofp_ports in "br->ofproto". */
-static void
-bridge_refresh_ofp_port(struct bridge *br)
-{
-    struct ofproto_port_dump dump;
-    struct ofproto_port ofproto_port;
-    struct port *port, *port_next;
-
-    /* Clear each "struct iface"s ofp_port so we can get its correct value. */
-    hmap_clear(&br->ifaces);
-    HMAP_FOR_EACH (port, hmap_node, &br->ports) {
-        struct iface *iface;
-
-        LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
-            iface->ofp_port = OFPP_NONE;
-        }
-    }
-
-    /* Obtain the correct "ofp_port"s from ofproto. Find any if_cfg's which
-     * already exist in the datapath and promote them to full fledged "struct
-     * iface"s.  Mark ports in the datapath which don't belong as garbage. */
-    OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, br->ofproto) {
-        if (!bridge_refresh_one_ofp_port(br, &ofproto_port)) {
-            struct ofpp_garbage *garbage = xmalloc(sizeof *garbage);
-            garbage->ofp_port = ofproto_port.ofp_port;
-            list_push_front(&br->ofpp_garbage, &garbage->list_node);
-        }
-    }
-
-    /* Some ifaces may not have "ofp_port"s in ofproto and therefore don't
-     * deserve to have "struct iface"s.  Demote these to "if_cfg"s so that
-     * later they can be added to ofproto. */
-    HMAP_FOR_EACH_SAFE (port, port_next, hmap_node, &br->ports) {
-        struct iface *iface, *iface_next;
-
-        LIST_FOR_EACH_SAFE (iface, iface_next, port_elem, &port->ifaces) {
-            if (iface->ofp_port == OFPP_NONE) {
-                bridge_queue_if_cfg(br, iface->cfg, port->cfg);
-                iface_destroy(iface);
-            }
-        }
-
-        if (list_is_empty(&port->ifaces)) {
-            port_destroy(port);
-        }
-    }
+    return netdev_set_config(netdev, &iface_cfg->options);
 }
 
 /* Opens a network device for 'if_cfg' and configures it.  If '*ofp_portp'
@@ -1470,11 +1411,10 @@ bridge_refresh_ofp_port(struct bridge *br)
  * failure, returns a positive errno value and stores NULL in '*netdevp'. */
 static int
 iface_do_create(const struct bridge *br,
-                const struct if_cfg *if_cfg,
+                const struct ovsrec_interface *iface_cfg,
+                const struct ovsrec_port *port_cfg,
                 ofp_port_t *ofp_portp, struct netdev **netdevp)
 {
-    const struct ovsrec_interface *iface_cfg = if_cfg->cfg;
-    const struct ovsrec_port *port_cfg = if_cfg->parent;
     struct netdev *netdev = NULL;
     int error;
 
@@ -1498,22 +1438,15 @@ iface_do_create(const struct bridge *br,
         goto error;
     }
 
-    if (*ofp_portp == OFPP_NONE) {
-        ofp_port_t ofp_port = if_cfg->ofport;
-
-        error = ofproto_port_add(br->ofproto, netdev, &ofp_port);
-        if (error) {
-            goto error;
-        }
-        *ofp_portp = ofp_port;
-
-        VLOG_INFO("bridge %s: added interface %s on port %d",
-                  br->name, iface_cfg->name, *ofp_portp);
-    } else {
-        VLOG_DBG("bridge %s: interface %s is on port %d",
-                 br->name, iface_cfg->name, *ofp_portp);
+    *ofp_portp = iface_pick_ofport(iface_cfg);
+    error = ofproto_port_add(br->ofproto, netdev, ofp_portp);
+    if (error) {
+        goto error;
     }
 
+    VLOG_INFO("bridge %s: added interface %s on port %d",
+              br->name, iface_cfg->name, *ofp_portp);
+
     if ((port_cfg->vlan_mode && !strcmp(port_cfg->vlan_mode, "splinter"))
         || iface_is_internal(iface_cfg, br->cfg)) {
         netdev_turn_flags_on(netdev, NETDEV_UP, NULL);
@@ -1535,31 +1468,22 @@ error:
  *
  * Return true if an iface is successfully created, false otherwise. */
 static bool
-iface_create(struct bridge *br, struct if_cfg *if_cfg, ofp_port_t ofp_port)
+iface_create(struct bridge *br, const struct ovsrec_interface *iface_cfg,
+             const struct ovsrec_port *port_cfg)
 {
-    const struct ovsrec_interface *iface_cfg = if_cfg->cfg;
-    const struct ovsrec_port *port_cfg = if_cfg->parent;
-
     struct netdev *netdev;
     struct iface *iface;
+    ofp_port_t ofp_port;
     struct port *port;
     int error;
-    bool ok = true;
 
-    /* Do the bits that can fail up front.
-     *
-     * It's a bit dangerous to call bridge_run_fast() here as ofproto's
-     * internal datastructures may not be consistent.  Eventually, when port
-     * additions and deletions are cheaper, these calls should be removed. */
-    bridge_run_fast();
+    /* Do the bits that can fail up front. */
     ovs_assert(!iface_lookup(br, iface_cfg->name));
-    error = iface_do_create(br, if_cfg, &ofp_port, &netdev);
-    bridge_run_fast();
+    error = iface_do_create(br, iface_cfg, port_cfg, &ofp_port, &netdev);
     if (error) {
         iface_set_ofport(iface_cfg, OFPP_NONE);
         iface_clear_db_record(iface_cfg);
-        ok = false;
-        goto done;
+        return false;
     }
 
     /* Get or create the port structure. */
@@ -1575,12 +1499,14 @@ iface_create(struct bridge *br, struct if_cfg *if_cfg, ofp_port_t ofp_port)
                 hash_string(iface_cfg->name, 0));
     iface->port = port;
     iface->name = xstrdup(iface_cfg->name);
-    iface->ofp_port = OFPP_NONE;
+    iface->ofp_port = ofp_port;
     iface->netdev = netdev;
     iface->type = iface_get_type(iface_cfg, br->cfg);
     iface->cfg = iface_cfg;
+    hmap_insert(&br->ifaces, &iface->ofp_port_node,
+                hash_ofp_port(ofp_port));
 
-    iface_set_ofp_port(iface, ofp_port);
+    iface_set_ofport(iface->cfg, ofp_port);
 
     /* Populate initial status in database. */
     iface_refresh_stats(iface);
@@ -1597,8 +1523,7 @@ iface_create(struct bridge *br, struct if_cfg *if_cfg, ofp_port_t ofp_port)
 
             error = netdev_open(port->name, "internal", &netdev);
             if (!error) {
-                ofp_port_t fake_ofp_port = if_cfg->ofport;
-
+                ofp_port_t fake_ofp_port = OFPP_NONE;
                 ofproto_port_add(br->ofproto, netdev, &fake_ofp_port);
                 netdev_close(netdev);
             } else {
@@ -1611,11 +1536,7 @@ iface_create(struct bridge *br, struct if_cfg *if_cfg, ofp_port_t ofp_port)
         }
     }
 
-done:
-    hmap_remove(&br->if_cfg_todo, &if_cfg->hmap_node);
-    free(if_cfg);
-
-    return ok;
+    return true;
 }
 
 /* Set forward BPDU option. */
@@ -2032,8 +1953,6 @@ port_refresh_stp_status(struct port *port)
     struct ofproto *ofproto = port->bridge->ofproto;
     struct iface *iface;
     struct ofproto_port_stp_status status;
-    char *keys[3];
-    int64_t int_values[3];
     struct smap smap;
 
     if (port_is_synthetic(port)) {
@@ -2047,14 +1966,12 @@ port_refresh_stp_status(struct port *port)
     }
 
     iface = CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem);
-
     if (ofproto_port_get_stp_status(ofproto, iface->ofp_port, &status)) {
         return;
     }
 
     if (!status.enabled) {
         ovsrec_port_set_status(port->cfg, NULL);
-        ovsrec_port_set_statistics(port->cfg, NULL, NULL, 0);
         return;
     }
 
@@ -2066,14 +1983,43 @@ port_refresh_stp_status(struct port *port)
     smap_add(&smap, "stp_role", stp_role_name(status.role));
     ovsrec_port_set_status(port->cfg, &smap);
     smap_destroy(&smap);
+}
+
+static void
+port_refresh_stp_stats(struct port *port)
+{
+    struct ofproto *ofproto = port->bridge->ofproto;
+    struct iface *iface;
+    struct ofproto_port_stp_stats stats;
+    char *keys[3];
+    int64_t int_values[3];
+
+    if (port_is_synthetic(port)) {
+        return;
+    }
+
+    /* STP doesn't currently support bonds. */
+    if (!list_is_singleton(&port->ifaces)) {
+        return;
+    }
+
+    iface = CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem);
+    if (ofproto_port_get_stp_stats(ofproto, iface->ofp_port, &stats)) {
+        return;
+    }
+
+    if (!stats.enabled) {
+        ovsrec_port_set_statistics(port->cfg, NULL, NULL, 0);
+        return;
+    }
 
     /* Set Statistics column. */
     keys[0] = "stp_tx_count";
-    int_values[0] = status.tx_count;
+    int_values[0] = stats.tx_count;
     keys[1] = "stp_rx_count";
-    int_values[1] = status.rx_count;
+    int_values[1] = stats.rx_count;
     keys[2] = "stp_error_count";
-    int_values[2] = status.error_count;
+    int_values[2] = stats.error_count;
 
     ovsrec_port_set_statistics(port->cfg, keys, int_values,
                                ARRAY_SIZE(int_values));
@@ -2118,7 +2064,7 @@ run_system_stats(void)
     }
 }
 
-static inline const char *
+static const char *
 ofp12_controller_role_to_str(enum ofp12_controller_role role)
 {
     switch (role) {
@@ -2225,12 +2171,19 @@ instant_stats_run(void)
 
     if (!instant_txn) {
         struct bridge *br;
+        uint64_t seq;
 
         if (time_msec() < instant_next_txn) {
             return;
         }
         instant_next_txn = time_msec() + INSTANT_INTERVAL_MSEC;
 
+        seq = seq_read(connectivity_seq_get());
+        if (seq == connectivity_seqno) {
+            return;
+        }
+        connectivity_seqno = seq;
+
         instant_txn = ovsdb_idl_txn_create(idl);
         HMAP_FOR_EACH (br, node, &all_bridges) {
             struct iface *iface;
@@ -2307,28 +2260,24 @@ instant_stats_wait(void)
     }
 }
 \f
-/* Performs periodic activity required by bridges that needs to be done with
- * the least possible latency.
- *
- * It makes sense to call this function a couple of times per poll loop, to
- * provide a significant performance boost on some benchmarks with ofprotos
- * that use the ofproto-dpif implementation. */
-void
-bridge_run_fast(void)
+static void
+bridge_run__(void)
 {
+    struct bridge *br;
     struct sset types;
     const char *type;
-    struct bridge *br;
 
+    /* Let each datapath type do the work that it needs to do. */
     sset_init(&types);
     ofproto_enumerate_types(&types);
     SSET_FOR_EACH (type, &types) {
-        ofproto_type_run_fast(type);
+        ofproto_type_run(type);
     }
     sset_destroy(&types);
 
+    /* Let each bridge do the work that it needs to do. */
     HMAP_FOR_EACH (br, node, &all_bridges) {
-        ofproto_run_fast(br->ofproto);
+        ofproto_run(br->ofproto);
     }
 }
 
@@ -2337,38 +2286,32 @@ bridge_run(void)
 {
     static struct ovsrec_open_vswitch null_cfg;
     const struct ovsrec_open_vswitch *cfg;
-    struct ovsdb_idl_txn *reconf_txn = NULL;
-    struct sset types;
-    const char *type;
 
     bool vlan_splinters_changed;
     struct bridge *br;
 
     ovsrec_open_vswitch_init(&null_cfg);
 
-    /* (Re)configure if necessary. */
-    if (!reconfiguring) {
-        ovsdb_idl_run(idl);
+    ovsdb_idl_run(idl);
 
-        if (ovsdb_idl_is_lock_contended(idl)) {
-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-            struct bridge *br, *next_br;
+    if (ovsdb_idl_is_lock_contended(idl)) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+        struct bridge *br, *next_br;
 
-            VLOG_ERR_RL(&rl, "another ovs-vswitchd process is running, "
-                        "disabling this process (pid %ld) until it goes away",
-                        (long int) getpid());
+        VLOG_ERR_RL(&rl, "another ovs-vswitchd process is running, "
+                    "disabling this process (pid %ld) until it goes away",
+                    (long int) getpid());
 
-            HMAP_FOR_EACH_SAFE (br, next_br, node, &all_bridges) {
-                bridge_destroy(br);
-            }
-            /* Since we will not be running system_stats_run() in this process
-             * with the current situation of multiple ovs-vswitchd daemons,
-             * disable system stats collection. */
-            system_stats_enable(false);
-            return;
-        } else if (!ovsdb_idl_has_lock(idl)) {
-            return;
+        HMAP_FOR_EACH_SAFE (br, next_br, node, &all_bridges) {
+            bridge_destroy(br);
         }
+        /* Since we will not be running system_stats_run() in this process
+         * with the current situation of multiple ovs-vswitchd daemons,
+         * disable system stats collection. */
+        system_stats_enable(false);
+        return;
+    } else if (!ovsdb_idl_has_lock(idl)) {
+        return;
     }
     cfg = ovsrec_open_vswitch_first(idl);
 
@@ -2385,18 +2328,7 @@ bridge_run(void)
                                         "flow-restore-wait", false));
     }
 
-    /* Let each datapath type do the work that it needs to do. */
-    sset_init(&types);
-    ofproto_enumerate_types(&types);
-    SSET_FOR_EACH (type, &types) {
-        ofproto_type_run(type);
-    }
-    sset_destroy(&types);
-
-    /* Let each bridge do the work that it needs to do. */
-    HMAP_FOR_EACH (br, node, &all_bridges) {
-        ofproto_run(br->ofproto);
-    }
+    bridge_run__();
 
     /* Re-configure SSL.  We do this on every trip through the main loop,
      * instead of just when the database changes, because the contents of the
@@ -2411,59 +2343,39 @@ bridge_run(void)
         stream_ssl_set_ca_cert_file(ssl->ca_cert, ssl->bootstrap_ca_cert);
     }
 
-    if (!reconfiguring) {
-        /* If VLAN splinters are in use, then we need to reconfigure if VLAN
-         * usage has changed. */
-        vlan_splinters_changed = false;
-        if (vlan_splinters_enabled_anywhere) {
-            HMAP_FOR_EACH (br, node, &all_bridges) {
-                if (ofproto_has_vlan_usage_changed(br->ofproto)) {
-                    vlan_splinters_changed = true;
-                    break;
-                }
-            }
-        }
-
-        if (ovsdb_idl_get_seqno(idl) != idl_seqno || vlan_splinters_changed) {
-            idl_seqno = ovsdb_idl_get_seqno(idl);
-            if (cfg) {
-                reconf_txn = ovsdb_idl_txn_create(idl);
-                bridge_reconfigure(cfg);
-            } else {
-                /* We still need to reconfigure to avoid dangling pointers to
-                 * now-destroyed ovsrec structures inside bridge data. */
-                bridge_reconfigure(&null_cfg);
+    /* If VLAN splinters are in use, then we need to reconfigure if VLAN
+     * usage has changed. */
+    vlan_splinters_changed = false;
+    if (vlan_splinters_enabled_anywhere) {
+        HMAP_FOR_EACH (br, node, &all_bridges) {
+            if (ofproto_has_vlan_usage_changed(br->ofproto)) {
+                vlan_splinters_changed = true;
+                break;
             }
         }
     }
 
-    if (reconfiguring) {
-        if (!reconf_txn) {
-            reconf_txn = ovsdb_idl_txn_create(idl);
-        }
-
-        if (bridge_reconfigure_continue(cfg ? cfg : &null_cfg)) {
-            reconfiguring = false;
+    if (ovsdb_idl_get_seqno(idl) != idl_seqno || vlan_splinters_changed) {
+        struct ovsdb_idl_txn *txn;
 
-            if (cfg) {
-                ovsrec_open_vswitch_set_cur_cfg(cfg, cfg->next_cfg);
-            }
+        idl_seqno = ovsdb_idl_get_seqno(idl);
+        txn = ovsdb_idl_txn_create(idl);
+        bridge_reconfigure(cfg ? cfg : &null_cfg);
 
-            /* If we are completing our initial configuration for this run
-             * of ovs-vswitchd, then keep the transaction around to monitor
-             * it for completion. */
-            if (!initial_config_done) {
-                initial_config_done = true;
-                daemonize_txn = reconf_txn;
-                reconf_txn = NULL;
-            }
+        if (cfg) {
+            ovsrec_open_vswitch_set_cur_cfg(cfg, cfg->next_cfg);
         }
-    }
 
-    if (reconf_txn) {
-        ovsdb_idl_txn_commit(reconf_txn);
-        ovsdb_idl_txn_destroy(reconf_txn);
-        reconf_txn = NULL;
+        /* If we are completing our initial configuration for this run
+         * of ovs-vswitchd, then keep the transaction around to monitor
+         * it for completion. */
+        if (initial_config_done) {
+            ovsdb_idl_txn_commit(txn);
+            ovsdb_idl_txn_destroy(txn);
+        } else {
+            initial_config_done = true;
+            daemonize_txn = txn;
+        }
     }
 
     if (daemonize_txn) {
@@ -2499,6 +2411,8 @@ bridge_run(void)
                         iface_refresh_stats(iface);
                         iface_refresh_status(iface);
                     }
+
+                    port_refresh_stp_stats(port);
                 }
 
                 HMAP_FOR_EACH (m, hmap_node, &br->mirrors) {
@@ -2529,10 +2443,6 @@ bridge_wait(void)
         ovsdb_idl_txn_wait(daemonize_txn);
     }
 
-    if (reconfiguring) {
-        poll_immediate_wake();
-    }
-
     sset_init(&types);
     ofproto_enumerate_types(&types);
     SSET_FOR_EACH (type, &types) {
@@ -2559,6 +2469,15 @@ void
 bridge_get_memory_usage(struct simap *usage)
 {
     struct bridge *br;
+    struct sset types;
+    const char *type;
+
+    sset_init(&types);
+    ofproto_enumerate_types(&types);
+    SSET_FOR_EACH (type, &types) {
+        ofproto_type_get_memory_usage(type, usage);
+    }
+    sset_destroy(&types);
 
     HMAP_FOR_EACH (br, node, &all_bridges) {
         ofproto_get_memory_usage(br->ofproto, usage);
@@ -2680,9 +2599,6 @@ bridge_create(const struct ovsrec_bridge *br_cfg)
     hmap_init(&br->iface_by_name);
     hmap_init(&br->mirrors);
 
-    hmap_init(&br->if_cfg_todo);
-    list_init(&br->ofpp_garbage);
-
     hmap_insert(&all_bridges, &br->node, hash_string(br->name, 0));
 }
 
@@ -2692,8 +2608,6 @@ bridge_destroy(struct bridge *br)
     if (br) {
         struct mirror *mirror, *next_mirror;
         struct port *port, *next_port;
-        struct if_cfg *if_cfg, *next_if_cfg;
-        struct ofpp_garbage *garbage, *next_garbage;
 
         HMAP_FOR_EACH_SAFE (port, next_port, hmap_node, &br->ports) {
             port_destroy(port);
@@ -2701,15 +2615,6 @@ bridge_destroy(struct bridge *br)
         HMAP_FOR_EACH_SAFE (mirror, next_mirror, hmap_node, &br->mirrors) {
             mirror_destroy(mirror);
         }
-        HMAP_FOR_EACH_SAFE (if_cfg, next_if_cfg, hmap_node, &br->if_cfg_todo) {
-            hmap_remove(&br->if_cfg_todo, &if_cfg->hmap_node);
-            free(if_cfg);
-        }
-        LIST_FOR_EACH_SAFE (garbage, next_garbage, list_node,
-                            &br->ofpp_garbage) {
-            list_remove(&garbage->list_node);
-            free(garbage);
-        }
 
         hmap_remove(&all_bridges, &br->node);
         ofproto_destroy(br->ofproto);
@@ -2717,7 +2622,6 @@ bridge_destroy(struct bridge *br)
         hmap_destroy(&br->ports);
         hmap_destroy(&br->iface_by_name);
         hmap_destroy(&br->mirrors);
-        hmap_destroy(&br->if_cfg_todo);
         free(br->name);
         free(br->type);
         free(br);
@@ -2804,44 +2708,23 @@ bridge_get_controllers(const struct bridge *br,
 }
 
 static void
-bridge_queue_if_cfg(struct bridge *br,
-                    const struct ovsrec_interface *cfg,
-                    const struct ovsrec_port *parent)
+bridge_collect_wanted_ports(struct bridge *br,
+                            const unsigned long int *splinter_vlans,
+                            struct shash *wanted_ports)
 {
-    struct if_cfg *if_cfg = xmalloc(sizeof *if_cfg);
-
-    if_cfg->cfg = cfg;
-    if_cfg->parent = parent;
-    if_cfg->ofport = iface_pick_ofport(cfg);
-    hmap_insert(&br->if_cfg_todo, &if_cfg->hmap_node,
-                hash_string(if_cfg->cfg->name, 0));
-}
-
-/* Deletes "struct port"s and "struct iface"s under 'br' which aren't
- * consistent with 'br->cfg'.  Updates 'br->if_cfg_queue' with interfaces which
- * 'br' needs to complete its configuration. */
-static void
-bridge_add_del_ports(struct bridge *br,
-                     const unsigned long int *splinter_vlans)
-{
-    struct shash_node *port_node;
-    struct port *port, *next;
-    struct shash new_ports;
     size_t i;
 
-    ovs_assert(hmap_is_empty(&br->if_cfg_todo));
+    shash_init(wanted_ports);
 
-    /* Collect new ports. */
-    shash_init(&new_ports);
     for (i = 0; i < br->cfg->n_ports; i++) {
         const char *name = br->cfg->ports[i]->name;
-        if (!shash_add_once(&new_ports, name, br->cfg->ports[i])) {
+        if (!shash_add_once(wanted_ports, name, br->cfg->ports[i])) {
             VLOG_WARN("bridge %s: %s specified twice as bridge port",
                       br->name, name);
         }
     }
     if (bridge_get_controllers(br, NULL)
-        && !shash_find(&new_ports, br->name)) {
+        && !shash_find(wanted_ports, br->name)) {
         VLOG_WARN("bridge %s: no port named %s, synthesizing one",
                   br->name, br->name);
 
@@ -2857,17 +2740,27 @@ bridge_add_del_ports(struct bridge *br,
 
         br->synth_local_ifacep = &br->synth_local_iface;
 
-        shash_add(&new_ports, br->name, &br->synth_local_port);
+        shash_add(wanted_ports, br->name, &br->synth_local_port);
     }
 
     if (splinter_vlans) {
-        add_vlan_splinter_ports(br, splinter_vlans, &new_ports);
+        add_vlan_splinter_ports(br, splinter_vlans, wanted_ports);
     }
+}
+
+/* Deletes "struct port"s and "struct iface"s under 'br' which aren't
+ * consistent with 'br->cfg'.  Updates 'br->if_cfg_queue' with interfaces which
+ * 'br' needs to complete its configuration. */
+static void
+bridge_del_ports(struct bridge *br, const struct shash *wanted_ports)
+{
+    struct shash_node *port_node;
+    struct port *port, *next;
 
     /* Get rid of deleted ports.
      * Get rid of deleted interfaces on ports that still exist. */
     HMAP_FOR_EACH_SAFE (port, next, hmap_node, &br->ports) {
-        port->cfg = shash_find_data(&new_ports, port->name);
+        port->cfg = shash_find_data(wanted_ports, port->name);
         if (!port->cfg) {
             port_destroy(port);
         } else {
@@ -2875,9 +2768,8 @@ bridge_add_del_ports(struct bridge *br,
         }
     }
 
-    /* Update iface->cfg and iface->type in interfaces that still exist.
-     * Add new interfaces to creation queue. */
-    SHASH_FOR_EACH (port_node, &new_ports) {
+    /* Update iface->cfg and iface->type in interfaces that still exist. */
+    SHASH_FOR_EACH (port_node, wanted_ports) {
         const struct ovsrec_port *port = port_node->data;
         size_t i;
 
@@ -2895,12 +2787,10 @@ bridge_add_del_ports(struct bridge *br,
                                " dev@openvswitch.org with concerns.",
                                cfg->name);
             } else {
-                bridge_queue_if_cfg(br, cfg, port);
+                /* We will add new interfaces later. */
             }
         }
     }
-
-    shash_destroy(&new_ports);
 }
 
 /* Initializes 'oc' appropriately as a management service controller for
@@ -3140,7 +3030,7 @@ bridge_configure_tables(struct bridge *br)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
     int n_tables;
-    int i, j;
+    int i, j, k;
 
     n_tables = ofproto_get_n_tables(br->ofproto);
     j = 0;
@@ -3151,6 +3041,8 @@ bridge_configure_tables(struct bridge *br)
         s.max_flows = UINT_MAX;
         s.groups = NULL;
         s.n_groups = 0;
+        s.n_prefix_fields = 0;
+        memset(s.prefix_fields, ~0, sizeof(s.prefix_fields));
 
         if (j < br->cfg->n_flow_tables && i == br->cfg->key_flow_tables[j]) {
             struct ovsrec_flow_table *cfg = br->cfg->value_flow_tables[j++];
@@ -3161,7 +3053,6 @@ bridge_configure_tables(struct bridge *br)
             }
             if (cfg->overflow_policy
                 && !strcmp(cfg->overflow_policy, "evict")) {
-                size_t k;
 
                 s.groups = xmalloc(cfg->n_groups * sizeof *s.groups);
                 for (k = 0; k < cfg->n_groups; k++) {
@@ -3182,6 +3073,41 @@ bridge_configure_tables(struct bridge *br)
                     }
                 }
             }
+            /* Prefix lookup fields. */
+            s.n_prefix_fields = 0;
+            for (k = 0; k < cfg->n_prefixes; k++) {
+                const char *name = cfg->prefixes[k];
+                const struct mf_field *mf = mf_from_name(name);
+                if (!mf) {
+                    VLOG_WARN("bridge %s: 'prefixes' with unknown field: %s",
+                              br->name, name);
+                    continue;
+                }
+                if (mf->flow_be32ofs < 0 || mf->n_bits % 32) {
+                    VLOG_WARN("bridge %s: 'prefixes' with incompatible field: "
+                              "%s", br->name, name);
+                    continue;
+                }
+                if (s.n_prefix_fields >= ARRAY_SIZE(s.prefix_fields)) {
+                    VLOG_WARN("bridge %s: 'prefixes' with too many fields, "
+                              "field not used: %s", br->name, name);
+                    continue;
+                }
+                s.prefix_fields[s.n_prefix_fields++] = mf->id;
+            }
+            if (s.n_prefix_fields > 0) {
+                int k;
+                struct ds ds = DS_EMPTY_INITIALIZER;
+                for (k = 0; k < s.n_prefix_fields; k++) {
+                    if (k) {
+                        ds_put_char(&ds, ',');
+                    }
+                    ds_put_cstr(&ds, mf_from_id(s.prefix_fields[k])->name);
+                }
+                VLOG_INFO("bridge %s table %d: Prefix lookup with: %s.",
+                          br->name, i, ds_cstr(&ds));
+                ds_destroy(&ds);
+            }
         }
 
         ofproto_configure_table(br->ofproto, i, &s);
@@ -3204,6 +3130,8 @@ bridge_configure_dp_desc(struct bridge *br)
 \f
 /* Port functions. */
 
+static void iface_destroy__(struct iface *);
+
 static struct port *
 port_create(struct bridge *br, const struct ovsrec_port *cfg)
 {
@@ -3259,7 +3187,7 @@ port_destroy(struct port *port)
         }
 
         LIST_FOR_EACH_SAFE (iface, next, port_elem, &port->ifaces) {
-            iface_destroy(iface);
+            iface_destroy__(iface);
         }
 
         hmap_remove(&br->ports, &port->hmap_node);
@@ -3481,7 +3409,7 @@ iface_get_type(const struct ovsrec_interface *iface,
 }
 
 static void
-iface_destroy(struct iface *iface)
+iface_destroy__(struct iface *iface)
 {
     if (iface) {
         struct port *port = iface->port;
@@ -3505,6 +3433,19 @@ iface_destroy(struct iface *iface)
     }
 }
 
+static void
+iface_destroy(struct iface *iface)
+{
+    if (iface) {
+        struct port *port = iface->port;
+
+        iface_destroy__(iface);
+        if (list_is_empty(&port->ifaces)) {
+            port_destroy(port);
+        }
+    }
+}
+
 static struct iface *
 iface_lookup(const struct bridge *br, const char *name)
 {
@@ -3535,21 +3476,6 @@ iface_find(const char *name)
     return NULL;
 }
 
-static struct if_cfg *
-if_cfg_lookup(const struct bridge *br, const char *name)
-{
-    struct if_cfg *if_cfg;
-
-    HMAP_FOR_EACH_WITH_HASH (if_cfg, hmap_node, hash_string(name, 0),
-                             &br->if_cfg_todo) {
-        if (!strcmp(if_cfg->cfg->name, name)) {
-            return if_cfg;
-        }
-    }
-
-    return NULL;
-}
-
 static struct iface *
 iface_from_ofp_port(const struct bridge *br, ofp_port_t ofp_port)
 {
@@ -3774,14 +3700,27 @@ iface_is_synthetic(const struct iface *iface)
 }
 
 static ofp_port_t
-iface_pick_ofport(const struct ovsrec_interface *cfg)
+iface_validate_ofport__(size_t n, int64_t *ofport)
+{
+    return (n && *ofport >= 1 && *ofport < ofp_to_u16(OFPP_MAX)
+            ? u16_to_ofp(*ofport)
+            : OFPP_NONE);
+}
+
+static ofp_port_t
+iface_get_requested_ofp_port(const struct ovsrec_interface *cfg)
 {
-    ofp_port_t ofport = cfg->n_ofport ? u16_to_ofp(*cfg->ofport)
-                                      : OFPP_NONE;
-    return cfg->n_ofport_request ? u16_to_ofp(*cfg->ofport_request)
-                                 : ofport;
+    return iface_validate_ofport__(cfg->n_ofport_request, cfg->ofport_request);
 }
 
+static ofp_port_t
+iface_pick_ofport(const struct ovsrec_interface *cfg)
+{
+    ofp_port_t requested_ofport = iface_get_requested_ofp_port(cfg);
+    return (requested_ofport != OFPP_NONE
+            ? requested_ofport
+            : iface_validate_ofport__(cfg->n_ofport, cfg->ofport));
+}
 \f
 /* Port mirroring. */
 
index c1b0a2b..7bffee8 100644 (file)
@@ -22,7 +22,6 @@ void bridge_init(const char *remote);
 void bridge_exit(void);
 
 void bridge_run(void);
-void bridge_run_fast(void);
 void bridge_wait(void);
 
 void bridge_get_memory_usage(struct simap *usage);
index bc45dac..990e58f 100644 (file)
@@ -114,9 +114,7 @@ main(int argc, char *argv[])
             memory_report(&usage);
             simap_destroy(&usage);
         }
-        bridge_run_fast();
         bridge_run();
-        bridge_run_fast();
         unixctl_server_run(unixctl);
         netdev_run();
 
index 9392457..9eb21ed 100644 (file)
@@ -1,6 +1,6 @@
 {"name": "Open_vSwitch",
- "version": "7.3.0",
- "cksum": "2495231516 20311",
+ "version": "7.4.0",
+ "cksum": "951746691 20389",
  "tables": {
    "Open_vSwitch": {
      "columns": {
                          "enum": ["set", ["refuse", "evict"]]},
                  "min": 0, "max": 1}},
        "groups": {
-        "type": {"key": "string", "min": 0, "max": "unlimited"}}}},
+        "type": {"key": "string", "min": 0, "max": "unlimited"}},
+       "prefixes": {
+         "type": {"key": "string", "min": 0, "max": 3}}}},
    "QoS": {
      "columns": {
        "type": {
index f6a8db1..5fd82fc 100644 (file)
         </p>
       </column>
 
-      <column name="other_config" key="flow-eviction-threshold"
+      <column name="other_config" key="flow-limit"
               type='{"type": "integer", "minInteger": 0}'>
         <p>
-          A number of flows as a nonnegative integer.  This sets number of
-          flows at which eviction from the datapath flow table will be
-          triggered.  If there are a large number of flows then increasing this
-          value to around the number of flows present can result in reduced CPU
-          usage and packet loss.
+          The maximum
+          number of flows allowed in the datapath flow table.  Internally OVS
+          will choose a flow limit which will likely be lower than this number,
+          based on real time network conditions.
         </p>
         <p>
-          The default is 2500.  Values below 100 will be rounded up to 100.
+          The default is 200000.
         </p>
       </column>
 
               type='{"type": "integer", "minInteger": 1}'>
         <p>
           Specifies the number of threads for software datapaths to use for
-          handling new flows.  The default is two less than the number of
-          online CPU cores (but at least 1).
+          handling new flows.  The default the number of online CPU cores minus
+          the number of revalidators.
+        </p>
+        <p>
+          This configuration is per datapath.  If you have more than one
+          software datapath (e.g. some <code>system</code> bridges and some
+          <code>netdev</code> bridges), then the total number of threads is
+          <code>n-handler-threads</code> times the number of software
+          datapaths.
+        </p>
+      </column>
+
+      <column name="other_config" key="n-revalidator-threads"
+              type='{"type": "integer", "minInteger": 1}'>
+        <p>
+          Specifies the number of threads for software datapaths to use for
+          revalidating flows in the datapath.  Typically, there is a direct
+          correlation between the number of revalidator threads, and the number
+          of flows allowed in the datapath.  The default is the number of cpu
+          cores divided by four plus one.  If <code>n-handler-threads</code> is
+          set, the default changes to the number of cpu cores minus the number
+          of handler threads.
         </p>
         <p>
           This configuration is per datapath.  If you have more than one
          </p>
 
          <p>
-           Open vSwitch currently assigns the OpenFlow port number for an
-           interface once, when the client first adds the interface.  It does
-           not change the port number later if the client sets or changes or
-           clears <ref column="ofport_request"/>.  Therefore, to ensure that
-           <ref column="ofport_request"/> takes effect, the client should set
-           it in the same database transaction that creates the interface.
-           (Future versions of Open vSwitch might honor changes to <ref
-           column="ofport_request"/>.)
+           A client should ideally set this column's value in the same
+           database transaction that it uses to create the interface.  Open
+           vSwitch version 2.1 and later will honor a later request for a
+           specific port number, althuogh it might confuse some controllers:
+           OpenFlow does not have a way to announce a port number change, so
+           Open vSwitch represents it over OpenFlow as a port deletion
+           followed immediately by a port addition.
+         </p>
+
+         <p>
+           If <ref column="ofport_request"/> is set or changed to some other
+           port's automatically assigned port number, Open vSwitch chooses a
+           new port number for the latter port.
          </p>
        </column>
       </group>
       </p>
 
       <column name="cfm_mpid">
-        A Maintenance Point ID (MPID) uniquely identifies each endpoint within
-        a Maintenance Association.  The MPID is used to identify this endpoint
-        to other Maintenance Points in the MA.  Each end of a link being
-        monitored should have a different MPID.  Must be configured to enable
-        CFM on this <ref table="Interface"/>.
+        <p>
+          A Maintenance Point ID (MPID) uniquely identifies each endpoint
+          within a Maintenance Association.  The MPID is used to identify this
+          endpoint to other Maintenance Points in the MA.  Each end of a link
+          being monitored should have a different MPID.  Must be configured to
+          enable CFM on this <ref table="Interface"/>.
+        </p>
+        <p>
+          According to the 802.1ag specification, MPIDs can only range between
+          [1, 8191].  However, extended mode (see <ref column="other_config"
+          key="cfm_extended"/>) supports eight byte MPIDs.
+        </p>
       </column>
 
       <column name="cfm_flap_count">
         with compliant implementations which may be running concurrently on the
         network. Furthermore, extended mode increases the accuracy of the
         <code>cfm_interval</code> configuration parameter by breaking wire
-        compatibility with 802.1ag compliant implementations.  Defaults to
-        <code>false</code>.
+        compatibility with 802.1ag compliant implementations.  And extended
+        mode allows eight byte MPIDs.  Defaults to <code>false</code>.
       </column>
 
       <column name="other_config" key="cfm_demand" type='{"type": "boolean"}'>
         column has no effect.
       </p>
     </column>
+
+    <column name="prefixes">
+      <p>
+        This string set specifies which fields should be used for
+        address prefix tracking.  Prefix tracking allows the
+        classifier to skip rules with longer than necessary prefixes,
+        resulting in better wildcarding for datapath flows.
+      </p>
+      <p>
+        Prefix tracking may be beneficial when a flow table contains
+        matches on IP address fields with different prefix lengths.
+        For example, when a flow table contains IP address matches on
+        both full addresses and proper prefixes, the full address
+        matches will typically cause the datapath flow to un-wildcard
+        the whole address field (depending on flow entry priorities).
+        In this case each packet with a different address gets handed
+        to the userspace for flow processing and generates its own
+        datapath flow.  With prefix tracking enabled for the address
+        field in question packets with addresses matching shorter
+        prefixes would generate datapath flows where the irrelevant
+        address bits are wildcarded, allowing the same datapath flow
+        to handle all the packets within the prefix in question.  In
+        this case many userspace upcalls can be avoided and the
+        overall performance can be better.
+      </p>
+      <p>
+        This is a performance optimization only, so packets will
+        receive the same treatment with or without prefix tracking.
+      </p>
+      <p>
+        The supported fields are: <code>tun_id</code>,
+        <code>tun_src</code>, <code>tun_dst</code>,
+        <code>nw_src</code>, <code>nw_dst</code> (or aliases
+        <code>ip_src</code> and <code>ip_dst</code>),
+        <code>ipv6_src</code>, and <code>ipv6_dst</code>.  (Using this
+        feature for <code>tun_id</code> would only make sense if the
+        tunnel IDs have prefix structure similar to IP addresses.)
+      </p>
+      <p>
+        For example, <code>prefixes=ip_dst,ip_src</code> instructs the
+        flow classifier to track the IP destination and source
+        addresses used by the rules in this specific flow table.  To
+        set the prefix fields, the flow table record needs to exist:
+      </p>
+      <dl>
+        <dt><code>ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- --id=@N1 create Flow_Table name=table0</code></dt>
+        <dd>
+          Creates a flow table record for the OpenFlow table number 0.
+        </dd>
+
+        <dt><code>ovs-vsctl set Flow_Table table0 prefixes=ip_dst,ip_src</code></dt>
+        <dd>
+          Enables prefix tracking for IP source and destination
+          address fields.
+        </dd>
+      </dl>
+
+      <p>
+        There is a maximum number of fields that can be enabled for any
+        one flow table.  Currently this limit is 3.
+      </p>
+    </column>
   </table>
 
   <table name="QoS" title="Quality of Service configuration">
index 6e89a05..008f5b4 100644 (file)
@@ -11,7 +11,7 @@ man_MANS += \
    vtep/vtep-ctl.8
 
 vtep_vtep_ctl_SOURCES = vtep/vtep-ctl.c
-vtep_vtep_ctl_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+vtep_vtep_ctl_LDADD = lib/libopenvswitch.la $(SSL_LIBS)
 
 # ovs-vtep
 scripts_SCRIPTS += \
index 7b904f6..233367b 100644 (file)
@@ -3324,7 +3324,7 @@ post_create(struct vtep_ctl_context *ctx)
     struct uuid dummy;
 
     if (!uuid_from_string(&dummy, ds_cstr(&ctx->output))) {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
     real = ovsdb_idl_txn_get_insert_uuid(ctx->txn, &dummy);
     if (real) {
@@ -3441,7 +3441,7 @@ evaluate_relop(const struct ovsdb_datum *a, const struct ovsdb_datum *b,
         return ovsdb_datum_includes_all(b, a, type);
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -3739,7 +3739,7 @@ do_vtep_ctl(const char *args, struct vtep_ctl_command *commands,
     switch (status) {
     case TXN_UNCOMMITTED:
     case TXN_INCOMPLETE:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case TXN_ABORTED:
         /* Should not happen--we never call ovsdb_idl_txn_abort(). */
@@ -3760,7 +3760,7 @@ do_vtep_ctl(const char *args, struct vtep_ctl_command *commands,
         vtep_ctl_fatal("database not locked");
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
     free(error);
 
index 017183a..4a25f2f 100644 (file)
@@ -1,6 +1,6 @@
 {
   "name": "hardware_vtep",
-  "cksum": "1365749839 5604",
+  "cksum": "3096797177 6063",
   "tables": {
     "Global": {
       "columns": {
                   "min": 0, "max": "unlimited"}}},
       "isRoot": true,
       "indexes": [["name"]]},
+    "Arp_Sources_Local": {
+      "columns": {
+        "src_mac": {"type": "string"},
+      "locator": {
+        "type": {"key": {"type": "uuid",
+                         "refTable": "Physical_Locator"}}}},
+      "isRoot": true},
+    "Arp_Sources_Remote": {
+      "columns": {
+        "src_mac": {"type": "string"},
+      "locator": {
+        "type": {"key": {"type": "uuid",
+                         "refTable": "Physical_Locator"}}}},
+      "isRoot": true},
     "Physical_Locator_Set": {
       "columns": {
         "locators": {
           "ephemeral": true}},
       "indexes": [["target"]],
       "isRoot": false}},
-  "version": "1.1.0"}
+  "version": "1.2.0"}
index db8d408..39cbba1 100644 (file)
     </group>
   </table>
 
+  <table name="Arp_Sources_Local" title="ARP source addresses for logical routers">
+    <p>
+      MAC address to be used when a VTEP issues ARP requests on behalf
+      of a logical router.
+    </p>
+
+    <p>
+      A distributed logical router is implemented by a set of VTEPs
+      (both hardware VTEPs and vswitches). In order for a given VTEP
+      to populate the local ARP cache for a logical router, it issues
+      ARP requests with a source MAC address that is unique to the VTEP. A
+      single per-VTEP MAC can be re-used across all logical
+      networks. This table contains the MACs that are used by the
+      VTEPs of a given HSC. The table provides the mapping from MAC to
+      physical locator for each VTEP so that replies to the ARP
+      requests can be sent back to the correct VTEP using the
+      appropriate physical locator.
+    </p>
+
+    <column name="src_mac">
+      The source MAC to be used by a given VTEP.
+    </column>
+
+    <column name="locator">
+      The <ref table="Physical_Locator"/> to use for replies to ARP
+      requests from this MAC address.
+    </column>
+  </table>
+
+  <table name="Arp_Sources_Remote" title="ARP source addresses for logical routers">
+    <p>
+      MAC address to be used when a remote VTEP issues ARP requests on behalf
+      of a logical router.
+    </p>
+
+    <p>
+      This table is the remote counterpart of <ref
+      table="Arp_sources_local"/>. The NVC writes this table to notify
+      the HSC of the MACs that will be used by remote VTEPs when they
+      issue ARP requests on behalf of a distributed logical router.
+    </p>
+
+    <column name="src_mac">
+      The source MAC to be used by a given VTEP.
+    </column>
+
+    <column name="locator">
+      The <ref table="Physical_Locator"/> to use for replies to ARP
+      requests from this MAC address.
+    </column>
+  </table>
+
   <table name="Physical_Locator_Set">
     <p>
       A set of one or more <ref table="Physical_Locator"/>s.
index 2702601..ae29649 100644 (file)
@@ -130,6 +130,7 @@ rm \
     $RPM_BUILD_ROOT/usr/share/man/man8/ovs-l3ping.8 \
     $RPM_BUILD_ROOT/usr/share/man/man8/ovs-pki.8 \
     $RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8
+(cd "$RPM_BUILD_ROOT" && rm -f usr/lib/lib*)
 
 install -d -m 755 $RPM_BUILD_ROOT/var/lib/openvswitch