From 4f3240deab4e2bb336e00a00cca7fefe442241a1 Mon Sep 17 00:00:00 2001 From: haoluchen Date: Wed, 9 Oct 2024 11:54:31 +0800 Subject: [PATCH] =?UTF-8?q?dpu=E7=BD=91=E7=BB=9C=E5=8A=A0=E9=80=9Fovs=20pa?= =?UTF-8?q?tch=E5=BD=92=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- openvswitch-2.14.2-20241009.patch | 7083 +++++++++++++++++++++++++++++ 1 file changed, 7083 insertions(+) create mode 100644 openvswitch-2.14.2-20241009.patch diff --git a/openvswitch-2.14.2-20241009.patch b/openvswitch-2.14.2-20241009.patch new file mode 100644 index 0000000..a244f52 --- /dev/null +++ b/openvswitch-2.14.2-20241009.patch @@ -0,0 +1,7083 @@ +diff --git a/openvswitch-2.14.2/Makefile.in b/openvswitch-2.14.2/Makefile.in +index f9ce1c6..b151099 100644 +--- a/openvswitch-2.14.2/Makefile.in ++++ b/openvswitch-2.14.2/Makefile.in +@@ -256,15 +256,16 @@ noinst_PROGRAMS = $(am__EXEEXT_2) tests/test-ovsdb$(EXEEXT) \ + @HAVE_AF_XDP_TRUE@ lib/netdev-afxdp.c \ + @HAVE_AF_XDP_TRUE@ lib/netdev-afxdp.h + +-@DPDK_NETDEV_TRUE@am__append_23 = \ ++@HAVE_HWOFF_AGENT_TRUE@am__append_23 = -lhwoffagent ++@DPDK_NETDEV_TRUE@am__append_24 = \ + @DPDK_NETDEV_TRUE@ lib/dpdk.c \ + @DPDK_NETDEV_TRUE@ lib/netdev-dpdk.c \ + @DPDK_NETDEV_TRUE@ lib/netdev-offload-dpdk.c + +-@DPDK_NETDEV_FALSE@am__append_24 = \ ++@DPDK_NETDEV_FALSE@am__append_25 = \ + @DPDK_NETDEV_FALSE@ lib/dpdk-stub.c + +-@WIN32_TRUE@am__append_25 = \ ++@WIN32_TRUE@am__append_26 = \ + @WIN32_TRUE@ lib/dpif-netlink.c \ + @WIN32_TRUE@ lib/dpif-netlink.h \ + @WIN32_TRUE@ lib/dpif-netlink-rtnl.h \ +@@ -279,41 +280,41 @@ noinst_PROGRAMS = $(am__EXEEXT_2) tests/test-ovsdb$(EXEEXT) \ + @WIN32_TRUE@ lib/wmi.c \ + @WIN32_TRUE@ lib/wmi.h + +-@HAVE_POSIX_AIO_TRUE@am__append_26 = lib/async-append-aio.c +-@HAVE_POSIX_AIO_FALSE@am__append_27 = lib/async-append-null.c +-@HAVE_IF_DL_TRUE@am__append_28 = \ ++@HAVE_POSIX_AIO_TRUE@am__append_27 = lib/async-append-aio.c ++@HAVE_POSIX_AIO_FALSE@am__append_28 = lib/async-append-null.c ++@HAVE_IF_DL_TRUE@am__append_29 = \ + @HAVE_IF_DL_TRUE@ lib/if-notifier-bsd.c \ + @HAVE_IF_DL_TRUE@ lib/netdev-bsd.c \ + @HAVE_IF_DL_TRUE@ lib/rtbsd.c \ + @HAVE_IF_DL_TRUE@ lib/rtbsd.h \ + @HAVE_IF_DL_TRUE@ lib/route-table-bsd.c + +-@HAVE_OPENSSL_TRUE@am__append_29 = lib/stream-ssl.c lib/dhparams.c +-@HAVE_OPENSSL_FALSE@am__append_30 = lib/stream-nossl.c +-@HAVE_UNBOUND_TRUE@am__append_31 = lib/dns-resolve.c +-@HAVE_UNBOUND_FALSE@am__append_32 = lib/dns-resolve-stub.c +-@WIN32_TRUE@am__append_33 = ${PTHREAD_LIBS} +-@LINUX_TRUE@am__append_34 = utilities/nlmon +-@WIN32_FALSE@am__append_35 = \ ++@HAVE_OPENSSL_TRUE@am__append_30 = lib/stream-ssl.c lib/dhparams.c ++@HAVE_OPENSSL_FALSE@am__append_31 = lib/stream-nossl.c ++@HAVE_UNBOUND_TRUE@am__append_32 = lib/dns-resolve.c ++@HAVE_UNBOUND_FALSE@am__append_33 = lib/dns-resolve-stub.c ++@WIN32_TRUE@am__append_34 = ${PTHREAD_LIBS} ++@LINUX_TRUE@am__append_35 = utilities/nlmon ++@WIN32_FALSE@am__append_36 = \ + @WIN32_FALSE@ tests/test-unix-socket.c + +-@LINUX_TRUE@am__append_36 = \ ++@LINUX_TRUE@am__append_37 = \ + @LINUX_TRUE@ tests/test-netlink-conntrack.c + +-@HAVE_OPENSSL_TRUE@am__append_37 = $(TESTPKI_FILES) +-@HAVE_OPENSSL_TRUE@am__append_38 = $(TESTPKI_FILES) tests/ovs-pki.log +-@HAVE_OPENSSL_TRUE@am__append_39 = clean-pki ++@HAVE_OPENSSL_TRUE@am__append_38 = $(TESTPKI_FILES) ++@HAVE_OPENSSL_TRUE@am__append_39 = $(TESTPKI_FILES) tests/ovs-pki.log ++@HAVE_OPENSSL_TRUE@am__append_40 = clean-pki + + # OVS does not use C++ itself, but it provides public header files + # that a C++ compiler should accept, so when --enable-Werror is in + # effect and a C++ compiler is available, we build a C++ source file + # that #includes all the public headers, as a way to ensure that they + # are acceptable as C++. +-@HAVE_CXX_TRUE@am__append_40 = include/openvswitch/libcxxtest.la +-@HAVE_CXX_TRUE@am__append_41 = include/openvswitch/cxxtest.cc +-@HAVE_DOT_TRUE@am__append_42 = vswitchd/vswitch.gv vswitchd/vswitch.pic +-@HAVE_DOT_TRUE@am__append_43 = vtep/vtep.gv vtep/vtep.pic +-@WIN32_TRUE@am__append_44 = $(srcdir)/datapath-windows/include/OvsDpInterface.h ++@HAVE_CXX_TRUE@am__append_41 = include/openvswitch/libcxxtest.la ++@HAVE_CXX_TRUE@am__append_42 = include/openvswitch/cxxtest.cc ++@HAVE_DOT_TRUE@am__append_43 = vswitchd/vswitch.gv vswitchd/vswitch.pic ++@HAVE_DOT_TRUE@am__append_44 = vtep/vtep.gv vtep/vtep.pic ++@WIN32_TRUE@am__append_45 = $(srcdir)/datapath-windows/include/OvsDpInterface.h + subdir = . + ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 + am__aclocal_m4_deps = $(top_srcdir)/m4/absolute-header.m4 \ +@@ -409,7 +410,7 @@ am__DEPENDENCIES_1 = + @WIN32_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) + lib_libopenvswitch_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ +- $(am__DEPENDENCIES_2) $(am__append_16) ++ $(am__DEPENDENCIES_2) $(am__append_16) $(am__DEPENDENCIES_1) + am__lib_libopenvswitch_la_SOURCES_DIST = lib/aes128.c lib/aes128.h \ + lib/async-append.h lib/backtrace.c lib/backtrace.h lib/bfd.c \ + lib/bfd.h lib/bitmap.h lib/bundle.c lib/bundle.h \ +@@ -418,16 +419,17 @@ am__lib_libopenvswitch_la_SOURCES_DIST = lib/aes128.c lib/aes128.h \ + lib/ccmap.c lib/ccmap.h lib/cmap.c lib/cmap.h lib/colors.c \ + lib/colors.h lib/command-line.c lib/command-line.h \ + lib/compiler.h lib/connectivity.c lib/connectivity.h \ +- lib/conntrack-icmp.c lib/conntrack-private.h \ +- lib/conntrack-tcp.c lib/conntrack-tp.c lib/conntrack-tp.h \ +- lib/conntrack-other.c lib/conntrack.c lib/conntrack.h \ +- lib/coverage.c lib/coverage.h lib/crc32c.c lib/crc32c.h \ +- lib/csum.c lib/csum.h lib/ct-dpif.c lib/ct-dpif.h lib/daemon.c \ +- lib/daemon.h lib/daemon-private.h lib/db-ctl-base.c \ +- lib/db-ctl-base.h lib/dhcp.h lib/dummy.c lib/dummy.h \ +- lib/dhparams.h lib/dirs.h lib/dpctl.c lib/dpctl.h \ +- lib/dp-packet.h lib/dp-packet.c lib/dpdk.h \ +- lib/dpif-netdev-lookup.h lib/dpif-netdev-lookup.c \ ++ lib/hwoff_init_func.c lib/hwoff_init_func.h \ ++ lib/dpak_ovs.h lib/conntrack-icmp.c \ ++ lib/conntrack-private.h lib/conntrack-tcp.c lib/conntrack-tp.c \ ++ lib/conntrack-tp.h lib/conntrack-other.c lib/conntrack.c \ ++ lib/conntrack.h lib/ct_offload_provider.h lib/coverage.c \ ++ lib/coverage.h lib/crc32c.c lib/crc32c.h lib/csum.c lib/csum.h \ ++ lib/ct-dpif.c lib/ct-dpif.h lib/daemon.c lib/daemon.h \ ++ lib/daemon-private.h lib/db-ctl-base.c lib/db-ctl-base.h \ ++ lib/dhcp.h lib/dummy.c lib/dummy.h lib/dhparams.h lib/dirs.h \ ++ lib/dpctl.c lib/dpctl.h lib/dp-packet.h lib/dp-packet.c \ ++ lib/dpdk.h lib/dpif-netdev-lookup.h lib/dpif-netdev-lookup.c \ + lib/dpif-netdev-lookup-autovalidator.c \ + lib/dpif-netdev-lookup-generic.c lib/dpif-netdev.c \ + lib/dpif-netdev.h lib/dpif-netdev-private.h \ +@@ -563,8 +565,8 @@ am__lib_libopenvswitch_la_SOURCES_DIST = lib/aes128.c lib/aes128.h \ + am_lib_libopenvswitch_la_OBJECTS = lib/aes128.lo lib/backtrace.lo \ + lib/bfd.lo lib/bundle.lo lib/byteq.lo lib/cfm.lo \ + lib/classifier.lo lib/ccmap.lo lib/cmap.lo lib/colors.lo \ +- lib/command-line.lo lib/connectivity.lo lib/conntrack-icmp.lo \ +- lib/conntrack-tcp.lo lib/conntrack-tp.lo \ ++ lib/command-line.lo lib/connectivity.lo lib/hwoff_init_func.lo \ ++ lib/conntrack-icmp.lo lib/conntrack-tcp.lo lib/conntrack-tp.lo \ + lib/conntrack-other.lo lib/conntrack.lo lib/coverage.lo \ + lib/crc32c.lo lib/csum.lo lib/ct-dpif.lo lib/daemon.lo \ + lib/db-ctl-base.lo lib/dummy.lo lib/dpctl.lo lib/dp-packet.lo \ +@@ -807,7 +809,7 @@ am_tests_ovstest_OBJECTS = tests/ovstest.$(OBJEXT) \ + tests/test-stp.$(OBJEXT) tests/test-unixctl.$(OBJEXT) \ + tests/test-util.$(OBJEXT) tests/test-uuid.$(OBJEXT) \ + tests/test-bitmap.$(OBJEXT) tests/test-vconn.$(OBJEXT) \ +- tests/test-aa.$(OBJEXT) tests/test-stopwatch.$(OBJEXT) \ ++ tests/test-aa.$(OBJEXT) tests/test-stopwatch.$(OBJEXT) tests/test-hiovs-offload.$(OBJEXT) \ + $(am__objects_15) $(am__objects_16) + tests_ovstest_OBJECTS = $(am_tests_ovstest_OBJECTS) + tests_ovstest_DEPENDENCIES = lib/libopenvswitch.la +@@ -920,7 +922,8 @@ am__depfiles_remade = include/openvswitch/$(DEPDIR)/cxxtest.Plo \ + lib/$(DEPDIR)/guarded-list.Plo lib/$(DEPDIR)/hash.Plo \ + lib/$(DEPDIR)/heap.Plo lib/$(DEPDIR)/hindex.Plo \ + lib/$(DEPDIR)/hmap.Plo lib/$(DEPDIR)/hmapx.Plo \ +- lib/$(DEPDIR)/id-pool.Plo lib/$(DEPDIR)/if-notifier-bsd.Plo \ ++ lib/$(DEPDIR)/hwoff_init_func.Plo lib/$(DEPDIR)/id-pool.Plo \ ++ lib/$(DEPDIR)/if-notifier-bsd.Plo \ + lib/$(DEPDIR)/if-notifier-manual.Plo \ + lib/$(DEPDIR)/if-notifier-stub.Plo \ + lib/$(DEPDIR)/if-notifier.Plo lib/$(DEPDIR)/ipf.Plo \ +@@ -1563,7 +1566,7 @@ ALL_LOCAL = dist-hook-git config-h-check printf-check static-check \ + $(srcdir)/python/ovs/version.py $(srcdir)/python/ovs/dirs.py \ + vtep/vtep.ovsschema.stamp + BUILT_SOURCES = ofproto/ipfix-entities.def include/odp-netlink.h \ +- include/odp-netlink-macros.h $(OVSIDL_BUILT) $(am__append_44) ++ include/odp-netlink-macros.h $(OVSIDL_BUILT) $(am__append_45) + + # Clean up generated files from older OVS versions. (This is important so that + # #include "vswitch-idl.h" doesn't get the wrong copy.) +@@ -1583,10 +1586,10 @@ CLEANFILES = all-distfiles all-gitfiles missing-distfiles distfiles \ + utilities/ovs-tcpundump utilities/ovs-test \ + utilities/ovs-vlan-test utilities/ovs-vsctl.8 \ + utilities/bugtool/ovs-bugtool utilities/bugtool/ovs-bugtool.8 \ +- $(valgrind_wrappers) $(am__append_38) include/odp-netlink.h \ +- include/odp-netlink-macros.h $(HSTAMP_FILES) $(am__append_41) \ ++ $(valgrind_wrappers) $(am__append_39) include/odp-netlink.h \ ++ include/odp-netlink-macros.h $(HSTAMP_FILES) $(am__append_42) \ + cxx-check debian/copyright ipsec/ovs-monitor-ipsec \ +- vswitchd/ovs-vswitchd.8 $(am__append_42) \ ++ vswitchd/ovs-vswitchd.8 $(am__append_43) \ + vswitchd/ovs-vswitchd.conf.db.5 \ + vswitchd/vswitch.ovsschema.stamp vswitchd/vswitch-idl.c \ + vswitchd/vswitch-idl.h ovsdb/ovsdb-tool.1 ovsdb/ovsdb-client.1 \ +@@ -1594,7 +1597,7 @@ CLEANFILES = all-distfiles all-gitfiles missing-distfiles distfiles \ + ovsdb/ovsdb-dot ovsdb/_server.ovsschema.inc \ + ovsdb/_server.ovsschema.stamp ovsdb/ovsdb-server.5 \ + python/ovs/dirs.py vtep/vtep-ctl.8 vtep/ovs-vtep \ +- $(am__append_43) vtep/vtep.5 vtep/vtep.ovsschema.stamp \ ++ $(am__append_44) vtep/vtep.5 vtep/vtep.ovsschema.stamp \ + $(srcdir)/datapath-windows/include/OvsDpInterface.h \ + selinux/openvswitch-custom.te selinux/openvswitch-custom.pp \ + selinux/openvswitch-custom.fc selinux/openvswitch-custom.if +@@ -1602,7 +1605,7 @@ CLEANFILES = all-distfiles all-gitfiles missing-distfiles distfiles \ + # lcov support + # Requires build with --enable-coverage and lcov/genhtml in $PATH + CLEAN_LOCAL = clean-pycov $(am__append_10) $(am__append_13) clean-lcov \ +- $(am__append_39) ++ $(am__append_40) + DISTCLEANFILES = tests/atconfig tests/atlocal \ + rhel/usr_lib_systemd_system_ovs-vswitchd.service + PYCOV_CLEAN_FILES = build-aux/check-structs,cover \ +@@ -1986,7 +1989,7 @@ noinst_HEADERS = $(EXTRA_DIST) include/sparse/rte_byteorder.h \ + lib_LTLIBRARIES = lib/libopenvswitch.la $(am__append_15) \ + lib/libsflow.la ofproto/libofproto.la ovsdb/libovsdb.la \ + vtep/libvtep.la +-noinst_LTLIBRARIES = $(am__append_40) ++noinst_LTLIBRARIES = $(am__append_41) + noinst_man_MANS = + + # ovsdb-idlc +@@ -2015,7 +2018,7 @@ completion_SCRIPTS = utilities/ovs-appctl-bashcomp.bash \ + utilities/ovs-vsctl-bashcomp.bash + scripts_DATA = utilities/ovs-lib + SUFFIXES = .in .xml .h .hstamp .ovsidl .ovsschema +-check_DATA = $(am__append_37) ++check_DATA = $(am__append_38) + check_SCRIPTS = utilities/ovs-appctl-bashcomp.bash \ + utilities/ovs-vsctl-bashcomp.bash tests/atlocal + pkgconfig_DATA = lib/libopenvswitch.pc lib/libsflow.pc \ +@@ -2256,7 +2259,8 @@ extract_stem_and_section = \ + test -n "$$mandir" || { echo "unknown directory for manpage section $$section"; continue; } + + lib_libopenvswitch_la_LIBADD = $(SSL_LIBS) $(CAPNG_LDADD) \ +- $(LIBBPF_LDADD) $(am__append_14) $(am__append_16) ++ $(LIBBPF_LDADD) $(am__append_14) $(am__append_16) \ ++ $(am__append_23) + lib_libopenvswitch_la_LDFLAGS = \ + $(OVS_LTINFO) \ + -Wl,--version-script=$(top_builddir)/lib/libopenvswitch.sym \ +@@ -2286,16 +2290,17 @@ lib_libopenvswitch_la_SOURCES = lib/aes128.c lib/aes128.h \ + lib/ccmap.c lib/ccmap.h lib/cmap.c lib/cmap.h lib/colors.c \ + lib/colors.h lib/command-line.c lib/command-line.h \ + lib/compiler.h lib/connectivity.c lib/connectivity.h \ +- lib/conntrack-icmp.c lib/conntrack-private.h \ +- lib/conntrack-tcp.c lib/conntrack-tp.c lib/conntrack-tp.h \ +- lib/conntrack-other.c lib/conntrack.c lib/conntrack.h \ +- lib/coverage.c lib/coverage.h lib/crc32c.c lib/crc32c.h \ +- lib/csum.c lib/csum.h lib/ct-dpif.c lib/ct-dpif.h lib/daemon.c \ +- lib/daemon.h lib/daemon-private.h lib/db-ctl-base.c \ +- lib/db-ctl-base.h lib/dhcp.h lib/dummy.c lib/dummy.h \ +- lib/dhparams.h lib/dirs.h lib/dpctl.c lib/dpctl.h \ +- lib/dp-packet.h lib/dp-packet.c lib/dpdk.h \ +- lib/dpif-netdev-lookup.h lib/dpif-netdev-lookup.c \ ++ lib/hwoff_init_func.c lib/hwoff_init_func.h \ ++ lib/dpak_ovs.h lib/conntrack-icmp.c \ ++ lib/conntrack-private.h lib/conntrack-tcp.c lib/conntrack-tp.c \ ++ lib/conntrack-tp.h lib/conntrack-other.c lib/conntrack.c \ ++ lib/conntrack.h lib/ct_offload_provider.h lib/coverage.c \ ++ lib/coverage.h lib/crc32c.c lib/crc32c.h lib/csum.c lib/csum.h \ ++ lib/ct-dpif.c lib/ct-dpif.h lib/daemon.c lib/daemon.h \ ++ lib/daemon-private.h lib/db-ctl-base.c lib/db-ctl-base.h \ ++ lib/dhcp.h lib/dummy.c lib/dummy.h lib/dhparams.h lib/dirs.h \ ++ lib/dpctl.c lib/dpctl.h lib/dp-packet.h lib/dp-packet.c \ ++ lib/dpdk.h lib/dpif-netdev-lookup.h lib/dpif-netdev-lookup.c \ + lib/dpif-netdev-lookup-autovalidator.c \ + lib/dpif-netdev-lookup-generic.c lib/dpif-netdev.c \ + lib/dpif-netdev.h lib/dpif-netdev-private.h \ +@@ -2377,10 +2382,10 @@ lib_libopenvswitch_la_SOURCES = lib/aes128.c lib/aes128.h \ + lib/lldp/lldp-tlv.h lib/lldp/lldpd.c lib/lldp/lldpd.h \ + lib/lldp/lldpd-structs.c lib/lldp/lldpd-structs.h \ + $(am__append_17) $(am__append_18) $(am__append_21) \ +- $(am__append_22) $(am__append_23) $(am__append_24) \ +- $(am__append_25) $(am__append_26) $(am__append_27) \ +- $(am__append_28) $(am__append_29) $(am__append_30) \ +- lib/dns-resolve.h $(am__append_31) $(am__append_32) ++ $(am__append_22) $(am__append_24) $(am__append_25) \ ++ $(am__append_26) $(am__append_27) $(am__append_28) \ ++ $(am__append_29) $(am__append_30) $(am__append_31) \ ++ lib/dns-resolve.h $(am__append_32) $(am__append_33) + nodist_lib_libopenvswitch_la_SOURCES = \ + lib/dirs.c \ + lib/ovsdb-server-idl.c \ +@@ -2433,7 +2438,7 @@ ofproto_libofproto_la_SOURCES = ofproto/bond.c ofproto/bond.h \ + ofproto/bundles.c ofproto/bundles.h ofproto/ipfix-entities.def + ofproto_libofproto_la_CPPFLAGS = $(AM_CPPFLAGS) + ofproto_libofproto_la_CFLAGS = $(AM_CFLAGS) +-ofproto_libofproto_la_LIBADD = lib/libsflow.la $(am__append_33) ++ofproto_libofproto_la_LIBADD = lib/libsflow.la $(am__append_34) + dist_noinst_SCRIPTS = ofproto/ipfix-gen-entities + utilities_ovs_appctl_SOURCES = utilities/ovs-appctl.c + utilities_ovs_appctl_LDADD = lib/libopenvswitch.la +@@ -2692,7 +2697,7 @@ tests_ovstest_SOURCES = tests/ovstest.c tests/ovstest.h \ + tests/test-sha1.c tests/test-skiplist.c tests/test-stp.c \ + tests/test-unixctl.c tests/test-util.c tests/test-uuid.c \ + tests/test-bitmap.c tests/test-vconn.c tests/test-aa.c \ +- tests/test-stopwatch.c $(am__append_35) $(am__append_36) ++ tests/test-stopwatch.c $(am__append_36) $(am__append_37) + tests_ovstest_LDADD = lib/libopenvswitch.la + tests_test_stream_SOURCES = tests/test-stream.c + tests_test_stream_LDADD = lib/libopenvswitch.la +@@ -3257,6 +3262,8 @@ lib/cmap.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) + lib/colors.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) + lib/command-line.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) + lib/connectivity.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) ++lib/hwoff_init_func.lo: lib/$(am__dirstamp) \ ++ lib/$(DEPDIR)/$(am__dirstamp) + lib/conntrack-icmp.lo: lib/$(am__dirstamp) \ + lib/$(DEPDIR)/$(am__dirstamp) + lib/conntrack-tcp.lo: lib/$(am__dirstamp) \ +@@ -4218,6 +4225,7 @@ distclean-compile: + @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/hindex.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/hmap.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/hmapx.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/hwoff_init_func.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/id-pool.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/if-notifier-bsd.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/if-notifier-manual.Plo@am__quote@ # am--include-marker +@@ -5670,6 +5678,7 @@ distclean: distclean-recursive + -rm -f lib/$(DEPDIR)/hindex.Plo + -rm -f lib/$(DEPDIR)/hmap.Plo + -rm -f lib/$(DEPDIR)/hmapx.Plo ++ -rm -f lib/$(DEPDIR)/hwoff_init_func.Plo + -rm -f lib/$(DEPDIR)/id-pool.Plo + -rm -f lib/$(DEPDIR)/if-notifier-bsd.Plo + -rm -f lib/$(DEPDIR)/if-notifier-manual.Plo +@@ -6048,6 +6057,7 @@ maintainer-clean: maintainer-clean-recursive + -rm -f lib/$(DEPDIR)/hindex.Plo + -rm -f lib/$(DEPDIR)/hmap.Plo + -rm -f lib/$(DEPDIR)/hmapx.Plo ++ -rm -f lib/$(DEPDIR)/hwoff_init_func.Plo + -rm -f lib/$(DEPDIR)/id-pool.Plo + -rm -f lib/$(DEPDIR)/if-notifier-bsd.Plo + -rm -f lib/$(DEPDIR)/if-notifier-manual.Plo +diff --git a/openvswitch-2.14.2/acinclude.m4 b/openvswitch-2.14.2/acinclude.m4 +index 237b221..7b64d53 100644 +--- a/openvswitch-2.14.2/acinclude.m4 ++++ b/openvswitch-2.14.2/acinclude.m4 +@@ -334,8 +334,9 @@ dnl + dnl Configure DPDK source tree + AC_DEFUN([OVS_CHECK_DPDK], [ + AC_ARG_WITH([dpdk], +- [AC_HELP_STRING([--with-dpdk=/path/to/dpdk], +- [Specify the DPDK build directory])], ++ [AC_HELP_STRING([--with-dpdk=static|shared|yes], ++ [Specify "static" or "shared" depending on the ++ DPDK libraries to use])], + [have_dpdk=true]) + + AC_MSG_CHECKING([whether dpdk is enabled]) +@@ -345,27 +346,36 @@ AC_DEFUN([OVS_CHECK_DPDK], [ + else + AC_MSG_RESULT([yes]) + case "$with_dpdk" in +- yes) +- DPDK_AUTO_DISCOVER="true" +- PKG_CHECK_MODULES_STATIC([DPDK], [libdpdk], [ +- DPDK_INCLUDE="$DPDK_CFLAGS" +- DPDK_LIB="$DPDK_LIBS"], [ +- DPDK_INCLUDE="-I/usr/local/include/dpdk -I/usr/include/dpdk" +- DPDK_LIB="-ldpdk"]) +- ;; +- *) +- DPDK_AUTO_DISCOVER="false" +- DPDK_INCLUDE_PATH="$with_dpdk/include" +- # If 'with_dpdk' is passed install directory, point to headers +- # installed in $DESTDIR/$prefix/include/dpdk +- if test -e "$DPDK_INCLUDE_PATH/rte_config.h"; then +- DPDK_INCLUDE="-I$DPDK_INCLUDE_PATH" +- elif test -e "$DPDK_INCLUDE_PATH/dpdk/rte_config.h"; then +- DPDK_INCLUDE="-I$DPDK_INCLUDE_PATH/dpdk" +- fi +- DPDK_LIB_DIR="$with_dpdk/lib" +- DPDK_LIB="-ldpdk" +- ;; ++ "shared") ++ PKG_CHECK_MODULES([DPDK], [libdpdk], [ ++ DPDK_INCLUDE="$DPDK_CFLAGS" ++ DPDK_LIB="$DPDK_LIBS"]) ++ ;; ++ "static" | "yes") ++ PKG_CHECK_MODULES_STATIC([DPDK], [libdpdk], [ ++ DPDK_INCLUDE="$DPDK_CFLAGS" ++ DPDK_LIB="$DPDK_LIBS"]) ++ ++ dnl Statically linked private DPDK objects of form ++ dnl -l:file.a must be positioned between ++ dnl --whole-archive ... --no-whole-archive linker parameters. ++ dnl Old pkg-config versions misplace --no-whole-archive parameter ++ dnl and put it next to --whole-archive. ++ AC_MSG_CHECKING([for faulty pkg-config version]) ++ echo "$DPDK_LIB" | grep -q 'whole-archive.*l:lib.*no-whole-archive' ++ status=$? ++ case $status in ++ 0) ++ AC_MSG_RESULT([no]) ++ ;; ++ 1) ++ AC_MSG_RESULT([yes]) ++ AC_MSG_ERROR([Please upgrade pkg-config]) ++ ;; ++ *) ++ AC_MSG_ERROR([grep exited with status $status]) ++ ;; ++ esac + esac + + ovs_save_CFLAGS="$CFLAGS" +@@ -454,17 +464,15 @@ AC_DEFUN([OVS_CHECK_DPDK], [ + # This happens because the rest of the DPDK code doesn't use any symbol in + # the pmd driver objects, and the drivers register themselves using an + # __attribute__((constructor)) function. +- # +- # These options are specified inside a single -Wl directive to prevent +- # autotools from reordering them. +- # +- # OTOH newer versions of dpdk pkg-config (generated with Meson) +- # will already have flagged just the right set of libs with +- # --whole-archive - in those cases do not wrap it once more. +- case "$DPDK_LIB" in +- *whole-archive*) DPDK_vswitchd_LDFLAGS=$DPDK_LIB;; +- *) DPDK_vswitchd_LDFLAGS=-Wl,--whole-archive,$DPDK_LIB,--no-whole-archive +- esac ++ # Wrap the DPDK libraries inside a single -Wl directive ++ # after comma separation to prevent autotools from reordering them. ++ DPDK_vswitchd_LDFLAGS=$(echo "$DPDK_LIB"| tr -s ' ' ',' | sed 's/-Wl,//g') ++ # Replace -pthread with -lpthread for LD and remove the last extra comma. ++ DPDK_vswitchd_LDFLAGS=$(echo "$DPDK_vswitchd_LDFLAGS"| sed 's/,$//' | \ ++ sed 's/-pthread/-lpthread/g') ++ # Prepend "-Wl,". ++ DPDK_vswitchd_LDFLAGS="-Wl,$DPDK_vswitchd_LDFLAGS" ++ + AC_SUBST([DPDK_vswitchd_LDFLAGS]) + AC_DEFINE([DPDK_NETDEV], [1], [System uses the DPDK module.]) + fi +@@ -472,6 +480,22 @@ AC_DEFUN([OVS_CHECK_DPDK], [ + AM_CONDITIONAL([DPDK_NETDEV], test "$DPDKLIB_FOUND" = true) + ]) + ++dnl OVS_CHECK_HWOFF_AGENT ++dnl ++dnl Check whether we're building with ipu. ++AC_DEFUN([OVS_CHECK_HWOFF_AGENT], ++ [AC_ARG_ENABLE( ++ [hwoff], ++ [AC_HELP_STRING([--enable-hwoff], [Enable OVS hwoff support])], ++ [], [enable_hwoff=no]) ++ ++ if test "x$enable_hwoff" = xyes; then ++ AC_DEFINE([HAVE_HWOFF_AGENT], [1], [ovs enable hwoff]) ++ CFLAGS="$CFLAGS -I/usr/include/hwoff_agent" ++ fi ++ AM_CONDITIONAL([HAVE_HWOFF_AGENT], [test $enable_hwoff = yes]) ++ ]) ++ + dnl OVS_GREP_IFELSE(FILE, REGEX, [IF-MATCH], [IF-NO-MATCH]) + dnl + dnl Greps FILE for REGEX. If it matches, runs IF-MATCH, otherwise IF-NO-MATCH. +@@ -1220,7 +1244,7 @@ dnl with or without modifications, as long as this notice is preserved. + + AC_DEFUN([_OVS_CHECK_CC_OPTION], [dnl + m4_define([ovs_cv_name], [ovs_cv_[]m4_translit([$1], [-= ], [__])])dnl +- AC_CACHE_CHECK([whether $CC accepts $1], [ovs_cv_name], ++ AC_CACHE_CHECK([whether $CC accepts $1], [ovs_cv_name], + [ovs_save_CFLAGS="$CFLAGS" + dnl Include -Werror in the compiler options, because without -Werror + dnl clang's GCC-compatible compiler driver does not return a failure +@@ -1275,7 +1299,7 @@ dnl OVS_ENABLE_OPTION([OPTION]) + dnl Check whether the given C compiler OPTION is accepted. + dnl If so, add it to WARNING_FLAGS. + dnl Example: OVS_ENABLE_OPTION([-Wdeclaration-after-statement]) +-AC_DEFUN([OVS_ENABLE_OPTION], ++AC_DEFUN([OVS_ENABLE_OPTION], + [OVS_CHECK_CC_OPTION([$1], [WARNING_FLAGS="$WARNING_FLAGS $1"]) + AC_SUBST([WARNING_FLAGS])]) + +diff --git a/openvswitch-2.14.2/configure b/openvswitch-2.14.2/configure +index cb32f48..22658cc 100644 +--- a/openvswitch-2.14.2/configure ++++ b/openvswitch-2.14.2/configure +@@ -1599,8 +1599,9 @@ Optional Packages: + Specify the Linux kernel source directory (usually + figured out automatically from build directory) + +- --with-dpdk=/path/to/dpdk +- Specify the DPDK build directory ++ --with-dpdk=static|shared|yes ++ Specify "static" or "shared" depending on the DPDK ++ libraries to use + + Some influential environment variables: + CC C compiler command +@@ -30699,9 +30700,103 @@ $as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + $as_echo "yes" >&6; } + case "$with_dpdk" in +- yes) +- DPDK_AUTO_DISCOVER="true" +- _save_PKG_CONFIG=$PKG_CONFIG ++ "shared") ++ ++pkg_failed=no ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for DPDK" >&5 ++$as_echo_n "checking for DPDK... " >&6; } ++ ++if test -n "$DPDK_CFLAGS"; then ++ pkg_cv_DPDK_CFLAGS="$DPDK_CFLAGS" ++ elif test -n "$PKG_CONFIG"; then ++ if test -n "$PKG_CONFIG" && \ ++ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdpdk\""; } >&5 ++ ($PKG_CONFIG --exists --print-errors "libdpdk") 2>&5 ++ ac_status=$? ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; }; then ++ pkg_cv_DPDK_CFLAGS=`$PKG_CONFIG --cflags "libdpdk" 2>/dev/null` ++ test "x$?" != "x0" && pkg_failed=yes ++else ++ pkg_failed=yes ++fi ++ else ++ pkg_failed=untried ++fi ++if test -n "$DPDK_LIBS"; then ++ pkg_cv_DPDK_LIBS="$DPDK_LIBS" ++ elif test -n "$PKG_CONFIG"; then ++ if test -n "$PKG_CONFIG" && \ ++ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdpdk\""; } >&5 ++ ($PKG_CONFIG --exists --print-errors "libdpdk") 2>&5 ++ ac_status=$? ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; }; then ++ pkg_cv_DPDK_LIBS=`$PKG_CONFIG --libs "libdpdk" 2>/dev/null` ++ test "x$?" != "x0" && pkg_failed=yes ++else ++ pkg_failed=yes ++fi ++ else ++ pkg_failed=untried ++fi ++ ++ ++ ++if test $pkg_failed = yes; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } ++ ++if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then ++ _pkg_short_errors_supported=yes ++else ++ _pkg_short_errors_supported=no ++fi ++ if test $_pkg_short_errors_supported = yes; then ++ DPDK_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libdpdk" 2>&1` ++ else ++ DPDK_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libdpdk" 2>&1` ++ fi ++ # Put the nasty error message in config.log where it belongs ++ echo "$DPDK_PKG_ERRORS" >&5 ++ ++ as_fn_error $? "Package requirements (libdpdk) were not met: ++ ++$DPDK_PKG_ERRORS ++ ++Consider adjusting the PKG_CONFIG_PATH environment variable if you ++installed software in a non-standard prefix. ++ ++Alternatively, you may set the environment variables DPDK_CFLAGS ++and DPDK_LIBS to avoid the need to call pkg-config. ++See the pkg-config man page for more details." "$LINENO" 5 ++elif test $pkg_failed = untried; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } ++ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 ++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} ++as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it ++is in your PATH or set the PKG_CONFIG environment variable to the full ++path to pkg-config. ++ ++Alternatively, you may set the environment variables DPDK_CFLAGS ++and DPDK_LIBS to avoid the need to call pkg-config. ++See the pkg-config man page for more details. ++ ++To get pkg-config, see . ++See \`config.log' for more details" "$LINENO" 5; } ++else ++ DPDK_CFLAGS=$pkg_cv_DPDK_CFLAGS ++ DPDK_LIBS=$pkg_cv_DPDK_LIBS ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 ++$as_echo "yes" >&6; } ++ ++ DPDK_INCLUDE="$DPDK_CFLAGS" ++ DPDK_LIB="$DPDK_LIBS" ++fi ++ ;; ++ "static" | "yes") ++ _save_PKG_CONFIG=$PKG_CONFIG + PKG_CONFIG="$PKG_CONFIG --static" + + pkg_failed=no +@@ -31499,17 +31594,15 @@ $as_echo "$ovs_cv__mssse3" >&6; } + # This happens because the rest of the DPDK code doesn't use any symbol in + # the pmd driver objects, and the drivers register themselves using an + # __attribute__((constructor)) function. +- # +- # These options are specified inside a single -Wl directive to prevent +- # autotools from reordering them. +- # +- # OTOH newer versions of dpdk pkg-config (generated with Meson) +- # will already have flagged just the right set of libs with +- # --whole-archive - in those cases do not wrap it once more. +- case "$DPDK_LIB" in +- *whole-archive*) DPDK_vswitchd_LDFLAGS=$DPDK_LIB;; +- *) DPDK_vswitchd_LDFLAGS=-Wl,--whole-archive,$DPDK_LIB,--no-whole-archive +- esac ++ # Wrap the DPDK libraries inside a single -Wl directive ++ # after comma separation to prevent autotools from reordering them. ++ DPDK_vswitchd_LDFLAGS=$(echo "$DPDK_LIB"| tr -s ' ' ',' | sed 's/-Wl,//g') ++ # Replace -pthread with -lpthread for LD and remove the last extra comma. ++ DPDK_vswitchd_LDFLAGS=$(echo "$DPDK_vswitchd_LDFLAGS"| sed 's/,$//' | \ ++ sed 's/-pthread/-lpthread/g') ++ # Prepend "-Wl,". ++ DPDK_vswitchd_LDFLAGS="-Wl,$DPDK_vswitchd_LDFLAGS" ++ + + + $as_echo "#define DPDK_NETDEV 1" >>confdefs.h +diff --git a/openvswitch-2.14.2/configure.ac b/openvswitch-2.14.2/configure.ac +index ee52e33..44dcc97 100644 +--- a/openvswitch-2.14.2/configure.ac ++++ b/openvswitch-2.14.2/configure.ac +@@ -195,6 +195,7 @@ OVS_CHECK_LINUX_TC + OVS_CHECK_LINUX_SCTP_CT + OVS_CHECK_LINUX_VIRTIO_TYPES + OVS_CHECK_DPDK ++OVS_CHECK_HWOFF_AGENT + OVS_CHECK_PRAGMA_MESSAGE + AC_SUBST([OVS_CFLAGS]) + AC_SUBST([OVS_LDFLAGS]) +diff --git a/openvswitch-2.14.2/lib/adapter/ovs_adapter.h b/openvswitch-2.14.2/lib/adapter/ovs_adapter.h +new file mode 100644 +index 0000000..caa705c +--- /dev/null ++++ b/openvswitch-2.14.2/lib/adapter/ovs_adapter.h +@@ -0,0 +1,124 @@ ++/* ++ * Copyright (c) 2022-2022. Huawei Technologies Co., Ltd. ++ * Description: ovs adapter file ++ * ++ * 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. ++ * ++ * Create: 2022-09-24 ++ */ ++ ++#ifndef _OVS_ADAPTER_H ++#define _OVS_ADAPTER_H ++ ++#include ++#include "ovs-atomic.h" ++#include "cmap.h" ++#include "conntrack-private.h" ++#include "conntrack-tp.h" ++#include "conntrack.h" ++#include "dp-packet.h" ++#include "dpif-netdev.h" ++#include "hash.h" ++#include "latch.h" ++#include "mac-learning.h" ++#include "netdev-provider.h" ++#include "netdev-dpdk.h" ++#include "packets.h" ++#include "random.h" ++#include "ovs-rcu.h" ++#include "smap.h" ++#include "ovs-thread.h" ++#include "timeval.h" ++#include "unaligned.h" ++#include "unixctl.h" ++#include "util.h" ++#include "uuid.h" ++#include "openvswitch/dynamic-string.h" ++#include "openvswitch/hmap.h" ++#include "openvswitch/ofpbuf.h" ++#include "openvswitch/util.h" ++#include "openvswitch/poll-loop.h" ++#include "openvswitch/shash.h" ++ ++void atomic_count_init_ext(atomic_count *count, unsigned int value); ++unsigned int atomic_count_inc_ext(atomic_count *count); ++unsigned int atomic_count_dec_ext(atomic_count *count); ++unsigned int atomic_count_get_ext(atomic_count *count); ++void atomic_count_set_ext(atomic_count *count, unsigned int value); ++ ++struct cmap_node* cmap_node_next_ext(const struct cmap_node *node); ++struct cmap_node* cmap_node_next_protected_ext(const struct cmap_node *node); ++size_t cmap_remove_ext(struct cmap *cmap, struct cmap_node *node, uint32_t hash); ++ ++void conn_update_expiration_int(struct conntrack *ct, struct conn *conn, ++ int tm, long long now); ++void conn_update_expiration_no_lock_int(struct conntrack *ct, struct conn *conn, ++ int tm, long long now); ++uint32_t ct_get_hash_basis(struct conntrack *ct); ++struct conn_key* conn_get_key_addr(const struct conn *conn); ++struct conn_key* conn_get_rev_key_addr(const struct conn *conn); ++struct conn* conn_get_nat_conn(const struct conn *conn); ++void* conn_get_offload_info_addr(const struct conn *conn); ++void* conn_get_nat_info(const struct conn *conn); ++bool conn_get_cleaned(const struct conn *conn); ++struct ovs_mutex* conn_get_lock_addr(const struct conn *conn); ++int tcp_conn_timeout_get_int(const struct conn *conn); ++int icmp_conn_timeout_get_int(const struct conn *conn); ++int other_conn_timeout_get_int(const struct conn *conn); ++void ct_dpif_entry_set_print_offload(struct ct_dpif_entry *entry, ++ bool print_offload); ++void ct_dpif_entry_set_init_dir_offload_state(struct ct_dpif_entry *entry, ++ const char *state); ++void ct_dpif_entry_set_reply_dir_offload_state(struct ct_dpif_entry *entry, ++ const char *state); ++ ++size_t dp_packet_struct_size(void); ++void* dp_packet_data_ext(const struct dp_packet *b); ++struct rte_mbuf* dp_packet_get_mbuf_addr(const struct dp_packet *b); ++struct pkt_metadata* dp_packet_get_md_addr(const struct dp_packet *b); ++uint32_t dp_packet_size_ext(const struct dp_packet *b); ++ ++uint32_t hash_int_ext(uint32_t x, uint32_t basis); ++uint32_t hash_bytes_ext(const void *arg, size_t n_bytes, uint32_t basis); ++uint32_t hash_uint64_ext(const uint64_t x); ++uint32_t hash_add_ext(uint32_t hash, uint32_t data); ++ ++bool hmap_is_empty_ext(const struct hmap *hmap); ++size_t hmap_count_ext(const struct hmap *hmap); ++struct hmap_node* hmap_first_ext(const struct hmap *hmap); ++struct hmap_node* hmap_next_ext(const struct hmap *hmap, const struct hmap_node *node); ++void hmap_insert_at_ext(struct hmap *hmap, struct hmap_node *node, size_t hash, const char *where); ++void hmap_remove_ext(struct hmap *hmap, struct hmap_node *node); ++struct hmap_node* hmap_first_with_hash_ext(const struct hmap *hmap, size_t hash); ++struct hmap_node* hmap_next_with_hash_ext(const struct hmap *hmap); ++ ++const char* netdev_class_get_type(struct netdev_class *netdev_class); ++uint16_t netdev_class_get_if_index(struct netdev_class *netdev_class, struct netdev *netdev); ++ ++uint16_t vlan_tci_to_vid_ext(ovs_be16 vlan_tci); ++void ipv6_format_addr_ext(const struct in6_addr *addr, struct ds *ds); ++struct conn* pkt_metadata_get_conn(struct pkt_metadata *md); ++bool pkt_metadata_get_reply(struct pkt_metadata *md); ++bool* pkt_metadata_get_reply_addr(struct pkt_metadata *md); ++ ++void poll_timer_wait_ext(long long int msec); ++ ++bool ovsthread_once_start_ext(struct ovsthread_once *once); ++void ovs_mutex_lock_ext(const struct ovs_mutex *mutex); ++ ++ovs_be32 get_16aligned_be32_ext(const ovs_16aligned_be32 *x); ++ ++uint32_t hiovs_rte_flow_list_get_ext(ovs_u128 ufid_list[], struct rte_flow *flow_list[], uint32_t ufid_cnt); ++void hiovs_rte_flow_data_list_put_ext(void *flow_data_list[], uint32_t count); ++ ++#endif +\ No newline at end of file +diff --git a/openvswitch-2.14.2/lib/adapter/ovs_atomic_adapter.c b/openvswitch-2.14.2/lib/adapter/ovs_atomic_adapter.c +new file mode 100644 +index 0000000..3bcbec1 +--- /dev/null ++++ b/openvswitch-2.14.2/lib/adapter/ovs_atomic_adapter.c +@@ -0,0 +1,47 @@ ++/* ++ * Copyright (c) 2022-2022. Huawei Technologies Co., Ltd. ++ * Description: ovs atomic adapter file ++ * ++ * 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. ++ * ++ * Create: 2022-09-24 ++ */ ++ ++#include ++#include "ovs-atomic.h" ++#include "ovs_adapter.h" ++ ++void atomic_count_init_ext(atomic_count *count, unsigned int value) ++{ ++ atomic_count_init(count, value); ++} ++ ++unsigned int atomic_count_inc_ext(atomic_count *count) ++{ ++ return atomic_count_inc(count); ++} ++ ++unsigned int atomic_count_dec_ext(atomic_count *count) ++{ ++ return atomic_count_dec(count); ++} ++ ++unsigned int atomic_count_get_ext(atomic_count *count) ++{ ++ return atomic_count_get(count); ++} ++ ++void atomic_count_set_ext(atomic_count *count, unsigned int value) ++{ ++ atomic_count_set(count, value); ++} +diff --git a/openvswitch-2.14.2/lib/adapter/ovs_cmap_adapter.c b/openvswitch-2.14.2/lib/adapter/ovs_cmap_adapter.c +new file mode 100644 +index 0000000..c18a2f6 +--- /dev/null ++++ b/openvswitch-2.14.2/lib/adapter/ovs_cmap_adapter.c +@@ -0,0 +1,37 @@ ++/* ++ * Copyright (c) 2022-2022. Huawei Technologies Co., Ltd. ++ * Description: ovs cmap adapter file ++ * ++ * 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. ++ * ++ * Create: 2022-09-24 ++ */ ++ ++#include ++#include "cmap.h" ++#include "ovs_adapter.h" ++ ++struct cmap_node* cmap_node_next_ext(const struct cmap_node *node) ++{ ++ return cmap_node_next(node); ++} ++ ++struct cmap_node* cmap_node_next_protected_ext(const struct cmap_node *node) ++{ ++ return cmap_node_next_protected(node); ++} ++ ++size_t cmap_remove_ext(struct cmap *cmap, struct cmap_node *node, uint32_t hash) ++{ ++ return cmap_remove(cmap, node, hash); ++} +diff --git a/openvswitch-2.14.2/lib/adapter/ovs_conntrack_adapter.c b/openvswitch-2.14.2/lib/adapter/ovs_conntrack_adapter.c +new file mode 100644 +index 0000000..09fc667 +--- /dev/null ++++ b/openvswitch-2.14.2/lib/adapter/ovs_conntrack_adapter.c +@@ -0,0 +1,113 @@ ++/* ++ * Copyright (c) 2022-2022. Huawei Technologies Co., Ltd. ++ * Description: ovs conntrack adapter file ++ * ++ * 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. ++ * ++ * Create: 2022-09-24 ++ */ ++ ++#include ++#include "conntrack-private.h" ++#include "conntrack-tp.h" ++#include "conntrack.h" ++#include "ovs_adapter.h" ++ ++uint32_t conn_key_hash(const struct conn_key *, uint32_t basis); ++bool conn_key_lookup(struct conntrack *ct, const struct conn_key *key, ++ uint32_t hash, long long now, struct conn **conn_out, bool *reply); ++ ++void conn_update_expiration_int(struct conntrack *ct, struct conn *conn, ++ int tm, long long now) ++{ ++ conn_update_expiration(ct, conn, tm, now); ++} ++ ++void conn_update_expiration_no_lock_int(struct conntrack *ct, struct conn *conn, ++ int tm, long long now) ++{ ++ conn_update_expiration_no_lock(ct, conn, tm, now); ++} ++ ++uint32_t ct_get_hash_basis(struct conntrack *ct) ++{ ++ return ct->hash_basis; ++} ++ ++struct conn_key* conn_get_key_addr(const struct conn *conn) ++{ ++ return &conn->key; ++} ++ ++struct conn_key* conn_get_rev_key_addr(const struct conn *conn) ++{ ++ return &conn->rev_key; ++} ++ ++struct conn* conn_get_nat_conn(const struct conn *conn) ++{ ++ return conn->nat_conn; ++} ++ ++void* conn_get_offload_info_addr(const struct conn *conn) ++{ ++ return &conn->offload_info; ++} ++ ++void* conn_get_nat_info(const struct conn *conn) ++{ ++ return conn->nat_info; ++} ++ ++bool conn_get_cleaned(const struct conn *conn) ++{ ++ return conn->cleaned; ++} ++ ++struct ovs_mutex* conn_get_lock_addr(const struct conn *conn) ++{ ++ return &conn->lock; ++} ++ ++int tcp_conn_timeout_get_int(const struct conn *conn) ++{ ++ return (int)tcp_conn_timeout_get(conn); ++} ++ ++int icmp_conn_timeout_get_int(const struct conn *conn) ++{ ++ return (int)icmp_conn_timeout_get(conn); ++} ++ ++int other_conn_timeout_get_int(const struct conn *conn) ++{ ++ return (int)other_conn_timeout_get(conn); ++} ++ ++void ct_dpif_entry_set_print_offload(struct ct_dpif_entry *entry, ++ bool print_offload) ++{ ++ entry->print_offload = print_offload; ++} ++ ++void ct_dpif_entry_set_init_dir_offload_state(struct ct_dpif_entry *entry, ++ const char *state) ++{ ++ entry->init_dir_offload_state = state; ++} ++ ++void ct_dpif_entry_set_reply_dir_offload_state(struct ct_dpif_entry *entry, ++ const char *state) ++{ ++ entry->reply_dir_offload_state = state; ++} +diff --git a/openvswitch-2.14.2/lib/adapter/ovs_dp_adapter.c b/openvswitch-2.14.2/lib/adapter/ovs_dp_adapter.c +new file mode 100644 +index 0000000..00d54bb +--- /dev/null ++++ b/openvswitch-2.14.2/lib/adapter/ovs_dp_adapter.c +@@ -0,0 +1,48 @@ ++/* ++ * Copyright (c) 2022-2022. Huawei Technologies Co., Ltd. ++ * Description: ovs dp_packet adapter file ++ * ++ * 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. ++ * ++ * Create: 2022-09-24 ++ */ ++ ++#include "config.h" ++#include "dp-packet.h" ++#include "dpif-netdev.h" ++#include "ovs_adapter.h" ++ ++size_t dp_packet_struct_size(void) ++{ ++ return sizeof(struct dp_packet); ++} ++ ++void* dp_packet_data_ext(const struct dp_packet *b) ++{ ++ return dp_packet_data(b); ++} ++ ++struct rte_mbuf* dp_packet_get_mbuf_addr(const struct dp_packet *b) ++{ ++ return &b->mbuf; ++} ++ ++struct pkt_metadata* dp_packet_get_md_addr(const struct dp_packet *b) ++{ ++ return &b->md; ++} ++ ++uint32_t dp_packet_size_ext(const struct dp_packet *b) ++{ ++ return dp_packet_size(b); ++} +diff --git a/openvswitch-2.14.2/lib/adapter/ovs_hash_adapter.c b/openvswitch-2.14.2/lib/adapter/ovs_hash_adapter.c +new file mode 100644 +index 0000000..d638ff0 +--- /dev/null ++++ b/openvswitch-2.14.2/lib/adapter/ovs_hash_adapter.c +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (c) 2022-2022. Huawei Technologies Co., Ltd. ++ * Description: ovs hash adapter file ++ * ++ * 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. ++ * ++ * Create: 2022-09-24 ++ */ ++ ++#include "config.h" ++#include "hash.h" ++#include "ovs_adapter.h" ++ ++uint32_t hash_int_ext(uint32_t x, uint32_t basis) ++{ ++ return hash_int(x, basis); ++} ++ ++uint32_t hash_bytes_ext(const void *arg, size_t n_bytes, uint32_t basis) ++{ ++ return hash_bytes(arg, n_bytes, basis); ++} ++ ++uint32_t hash_uint64_ext(const uint64_t x) ++{ ++ return hash_uint64(x); ++} ++ ++uint32_t hash_add_ext(uint32_t hash, uint32_t data) ++{ ++ return hash_add(hash, data); ++} +diff --git a/openvswitch-2.14.2/lib/adapter/ovs_hiovs_flow_adapter.c b/openvswitch-2.14.2/lib/adapter/ovs_hiovs_flow_adapter.c +new file mode 100644 +index 0000000..9b228a4 +--- /dev/null ++++ b/openvswitch-2.14.2/lib/adapter/ovs_hiovs_flow_adapter.c +@@ -0,0 +1,31 @@ ++/* ++ * Copyright (c) 2022-2022. Huawei Technologies Co., Ltd. ++ * Description: ovs hiovs flow adapter file ++ * ++ * 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. ++ * ++ * Create: 2022-09-24 ++ */ ++#include "config.h" ++#include "openvswitch/util.h" ++#include "ovs_adapter.h" ++ ++uint32_t hiovs_rte_flow_list_get_ext(ovs_u128 ufid_list[], struct rte_flow *flow_list[], uint32_t ufid_cnt) ++{ ++ return hiovs_rte_flow_list_get(ufid_list, flow_list, ufid_cnt); ++} ++ ++void hiovs_rte_flow_data_list_put_ext(void *flow_data_list[], uint32_t count) ++{ ++ return hiovs_rte_flow_data_list_put(flow_data_list, count); ++} +\ No newline at end of file +diff --git a/openvswitch-2.14.2/lib/adapter/ovs_hmap_adapter.c b/openvswitch-2.14.2/lib/adapter/ovs_hmap_adapter.c +new file mode 100644 +index 0000000..fee67f9 +--- /dev/null ++++ b/openvswitch-2.14.2/lib/adapter/ovs_hmap_adapter.c +@@ -0,0 +1,62 @@ ++/* ++ * Copyright (c) 2022-2022. Huawei Technologies Co., Ltd. ++ * Description: ovs hmap adapter file ++ * ++ * 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. ++ * ++ * Create: 2022-09-24 ++ */ ++ ++#include "ovs_adapter.h" ++#include "config.h" ++#include "openvswitch/hmap.h" ++ ++bool hmap_is_empty_ext(const struct hmap *hmap) ++{ ++ return hmap_is_empty(hmap); ++} ++ ++size_t hmap_count_ext(const struct hmap *hmap) ++{ ++ return hmap_count(hmap); ++} ++ ++struct hmap_node* hmap_first_ext(const struct hmap *hmap) ++{ ++ return hmap_first(hmap); ++} ++ ++struct hmap_node* hmap_next_ext(const struct hmap *hmap, const struct hmap_node *node) ++{ ++ return hmap_next(hmap, node); ++} ++ ++void hmap_insert_at_ext(struct hmap *hmap, struct hmap_node *node, size_t hash, const char *where) ++{ ++ return hmap_insert_at(hmap, node, hash, where); ++} ++ ++void hmap_remove_ext(struct hmap *hmap, struct hmap_node *node) ++{ ++ return hmap_remove(hmap, node); ++} ++ ++struct hmap_node* hmap_first_with_hash_ext(const struct hmap *hmap, size_t hash) ++{ ++ return hmap_first_with_hash(hmap, hash); ++} ++ ++struct hmap_node* hmap_next_with_hash_ext(const struct hmap *hmap) ++{ ++ return hmap_next_with_hash(hmap); ++} +diff --git a/openvswitch-2.14.2/lib/adapter/ovs_netdev_adapter.c b/openvswitch-2.14.2/lib/adapter/ovs_netdev_adapter.c +new file mode 100644 +index 0000000..0692f6c +--- /dev/null ++++ b/openvswitch-2.14.2/lib/adapter/ovs_netdev_adapter.c +@@ -0,0 +1,32 @@ ++/* ++ * Copyright (c) 2022-2022. Huawei Technologies Co., Ltd. ++ * Description: ovs netdev adapter file ++ * ++ * 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. ++ * ++ * Create: 2022-09-24 ++ */ ++ ++#include "config.h" ++#include "netdev-provider.h" ++#include "ovs_adapter.h" ++ ++const char* netdev_class_get_type(struct netdev_class *netdev_class) ++{ ++ return netdev_class->type; ++} ++ ++uint16_t netdev_class_get_if_index(struct netdev_class *netdev_class, struct netdev *netdev) ++{ ++ return netdev_class->get_ifindex(netdev); ++} +diff --git a/openvswitch-2.14.2/lib/adapter/ovs_packet_adapter.c b/openvswitch-2.14.2/lib/adapter/ovs_packet_adapter.c +new file mode 100644 +index 0000000..89608d7 +--- /dev/null ++++ b/openvswitch-2.14.2/lib/adapter/ovs_packet_adapter.c +@@ -0,0 +1,47 @@ ++/* ++ * Copyright (c) 2022-2022. Huawei Technologies Co., Ltd. ++ * Description: ovs packet adapter file ++ * ++ * 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. ++ * ++ * Create: 2022-09-24 ++ */ ++ ++#include "config.h" ++#include "packets.h" ++#include "ovs_adapter.h" ++ ++uint16_t vlan_tci_to_vid_ext(ovs_be16 vlan_tci) ++{ ++ return vlan_tci_to_vid(vlan_tci); ++} ++ ++void ipv6_format_addr_ext(const struct in6_addr *addr, struct ds *ds) ++{ ++ ipv6_format_addr(addr, ds); ++} ++ ++struct conn* pkt_metadata_get_conn(struct pkt_metadata *md) ++{ ++ return md->conn; ++} ++ ++bool pkt_metadata_get_reply(struct pkt_metadata *md) ++{ ++ return md->reply; ++} ++ ++bool* pkt_metadata_get_reply_addr(struct pkt_metadata *md) ++{ ++ return &md->reply; ++} +diff --git a/openvswitch-2.14.2/lib/adapter/ovs_poll_adapter.c b/openvswitch-2.14.2/lib/adapter/ovs_poll_adapter.c +new file mode 100644 +index 0000000..a21f62b +--- /dev/null ++++ b/openvswitch-2.14.2/lib/adapter/ovs_poll_adapter.c +@@ -0,0 +1,27 @@ ++/* ++ * Copyright (c) 2022-2022. Huawei Technologies Co., Ltd. ++ * Description: ovs poll adapter file ++ * ++ * 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. ++ * ++ * Create: 2022-09-24 ++ */ ++#include "config.h" ++#include "openvswitch/util.h" ++#include "openvswitch/poll-loop.h" ++#include "ovs_adapter.h" ++ ++void poll_timer_wait_ext(long long int msec) ++{ ++ poll_timer_wait(msec); ++} +diff --git a/openvswitch-2.14.2/lib/adapter/ovs_thread_adapter.c b/openvswitch-2.14.2/lib/adapter/ovs_thread_adapter.c +new file mode 100644 +index 0000000..fa797c3 +--- /dev/null ++++ b/openvswitch-2.14.2/lib/adapter/ovs_thread_adapter.c +@@ -0,0 +1,31 @@ ++/* ++ * Copyright (c) 2022-2022. Huawei Technologies Co., Ltd. ++ * Description: ovs thread adapter file ++ * ++ * 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. ++ * ++ * Create: 2022-09-24 ++ */ ++#include "config.h" ++#include "ovs-thread.h" ++#include "ovs_adapter.h" ++ ++bool ovsthread_once_start_ext(struct ovsthread_once *once) ++{ ++ return ovsthread_once_start(once); ++} ++ ++void ovs_mutex_lock_ext(const struct ovs_mutex *mutex) ++{ ++ ovs_mutex_lock(mutex); ++} +diff --git a/openvswitch-2.14.2/lib/adapter/ovs_unaligned_adapter.c b/openvswitch-2.14.2/lib/adapter/ovs_unaligned_adapter.c +new file mode 100644 +index 0000000..9ab0760 +--- /dev/null ++++ b/openvswitch-2.14.2/lib/adapter/ovs_unaligned_adapter.c +@@ -0,0 +1,27 @@ ++/* ++ * Copyright (c) 2022-2022. Huawei Technologies Co., Ltd. ++ * Description: ovs unalign adapter file ++ * ++ * 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. ++ * ++ * Create: 2022-09-24 ++ */ ++ ++#include "config.h" ++#include "unaligned.h" ++#include "ovs_adapter.h" ++ ++ovs_be32 get_16aligned_be32_ext(const ovs_16aligned_be32 *x) ++{ ++ return get_16aligned_be32(x); ++} +diff --git a/openvswitch-2.14.2/lib/automake.mk b/openvswitch-2.14.2/lib/automake.mk +index 380a672..897bd1d 100644 +--- a/openvswitch-2.14.2/lib/automake.mk ++++ b/openvswitch-2.14.2/lib/automake.mk +@@ -82,6 +82,7 @@ lib_libopenvswitch_la_SOURCES = \ + lib/conntrack-other.c \ + lib/conntrack.c \ + lib/conntrack.h \ ++ lib/ct_offload_provider.h \ + lib/coverage.c \ + lib/coverage.h \ + lib/crc32c.c \ +@@ -365,7 +366,19 @@ lib_libopenvswitch_la_SOURCES = \ + lib/lldp/lldpd.c \ + lib/lldp/lldpd.h \ + lib/lldp/lldpd-structs.c \ +- lib/lldp/lldpd-structs.h ++ lib/lldp/lldpd-structs.h \ ++ lib/adapter/ovs_atomic_adapter.c \ ++ lib/adapter/ovs_cmap_adapter.c \ ++ lib/adapter/ovs_conntrack_adapter.c \ ++ lib/adapter/ovs_dp_adapter.c \ ++ lib/adapter/ovs_hash_adapter.c \ ++ lib/adapter/ovs_hmap_adapter.c \ ++ lib/adapter/ovs_netdev_adapter.c \ ++ lib/adapter/ovs_packet_adapter.c \ ++ lib/adapter/ovs_poll_adapter.c \ ++ lib/adapter/ovs_thread_adapter.c \ ++ lib/adapter/ovs_unaligned_adapter.c \ ++ lib/adapter/ovs_hiovs_flow_adapter.c + + if WIN32 + lib_libopenvswitch_la_SOURCES += \ +@@ -454,6 +467,14 @@ lib_libopenvswitch_la_SOURCES += \ + lib/netdev-afxdp.h + endif + ++if HAVE_HWOFF_AGENT ++lib_libopenvswitch_la_SOURCES += \ ++ lib/hwoff_init_func.c \ ++ lib/hwoff_init_func.h \ ++ lib/ct_dump_extend/ct_dump_extend.c \ ++ lib/ct_dump_extend/ct_dump_extend.h ++endif ++ + if DPDK_NETDEV + lib_libopenvswitch_la_SOURCES += \ + lib/dpdk.c \ +@@ -594,7 +615,7 @@ lib/meta-flow.inc: $(srcdir)/build-aux/extract-ofp-fields include/openvswitch/me + $(AM_V_GEN)$(run_python) $< meta-flow $(srcdir)/include/openvswitch/meta-flow.h > $@.tmp + $(AM_V_at)mv $@.tmp $@ + lib/meta-flow.lo: lib/meta-flow.inc +-lib/nx-match.inc: $(srcdir)/build-aux/extract-ofp-fields include/openvswitch/meta-flow.h ++lib/nx-match.inc: $(srcdir)/build-aux/extract-ofp-fields include/openvswitch/meta-flow.h + $(AM_V_GEN)$(run_python) $< nx-match $(srcdir)/include/openvswitch/meta-flow.h > $@.tmp + $(AM_V_at)mv $@.tmp $@ + lib/nx-match.lo: lib/nx-match.inc +diff --git a/openvswitch-2.14.2/lib/conntrack-icmp.c b/openvswitch-2.14.2/lib/conntrack-icmp.c +index b402970..4ea703c 100644 +--- a/openvswitch-2.14.2/lib/conntrack-icmp.c ++++ b/openvswitch-2.14.2/lib/conntrack-icmp.c +@@ -104,3 +104,9 @@ struct ct_l4_proto ct_proto_icmp6 = { + .valid_new = icmp6_valid_new, + .conn_update = icmp_conn_update, + }; ++ ++enum ct_timeout icmp_conn_timeout_get(const struct conn *conn) ++{ ++ struct conn_icmp *conn_icmp = conn_icmp_cast(conn); ++ return icmp_timeouts[conn_icmp->state]; ++} +diff --git a/openvswitch-2.14.2/lib/conntrack-other.c b/openvswitch-2.14.2/lib/conntrack-other.c +index d3b4601..67c7405 100644 +--- a/openvswitch-2.14.2/lib/conntrack-other.c ++++ b/openvswitch-2.14.2/lib/conntrack-other.c +@@ -88,3 +88,10 @@ struct ct_l4_proto ct_proto_other = { + .valid_new = other_valid_new, + .conn_update = other_conn_update, + }; ++ ++enum ct_timeout other_conn_timeout_get(const struct conn *conn) ++{ ++ struct conn_other *conn_other = conn_other_cast(conn); ++ return other_timeouts[conn_other->state]; ++} ++ +diff --git a/openvswitch-2.14.2/lib/conntrack-private.h b/openvswitch-2.14.2/lib/conntrack-private.h +index 3434753..f74eea5 100644 +--- a/openvswitch-2.14.2/lib/conntrack-private.h ++++ b/openvswitch-2.14.2/lib/conntrack-private.h +@@ -32,6 +32,10 @@ + #include "unaligned.h" + #include "dp-packet.h" + ++#ifdef HAVE_HWOFF_AGENT ++#include "ct_offload_provider.h" ++#endif ++ + struct ct_endpoint { + union ct_addr addr; + union { +@@ -88,6 +92,14 @@ enum OVS_PACKED_ENUM ct_conn_type { + CT_CONN_TYPE_UN_NAT, + }; + ++#ifdef HAVE_HWOFF_AGENT ++typedef struct { ++ conn_id conn_id; ++ bool is_ct_established; ++ void *conn_private_data; ++} ct_offload_info; ++#endif ++ + struct conn { + /* Immutable data. */ + struct conn_key key; +@@ -120,6 +132,17 @@ struct conn { + enum ct_conn_type conn_type; + + uint32_t tp_id; /* Timeout policy ID. */ ++#ifdef HAVE_HWOFF_AGENT ++ ct_offload_info offload_info; ++#endif ++}; ++ ++struct conn_lookup_ctx { ++ struct conn_key key; ++ struct conn *conn; ++ uint32_t hash; ++ bool reply; ++ bool icmp_related; + }; + + enum ct_update_res { +@@ -177,8 +200,15 @@ struct conntrack { + struct ipf *ipf; /* Fragmentation handling context. */ + uint32_t zone_limit_seq; /* Used to disambiguate zone limit counts. */ + atomic_bool tcp_seq_chk; /* Check TCP sequence numbers. */ ++#ifdef HAVE_HWOFF_AGENT ++ ct_offload_class *offload_class; ++#endif + }; + ++#ifdef HAVE_HWOFF_AGENT ++void reg_ct_offload_class(ct_offload_class *class); ++#endif ++ + /* Lock acquisition order: + * 1. 'ct_lock' + * 2. 'conn->lock' +@@ -213,4 +243,7 @@ tcp_payload_length(struct dp_packet *pkt) + } + } + ++enum ct_timeout tcp_conn_timeout_get(const struct conn *conn); ++enum ct_timeout icmp_conn_timeout_get(const struct conn *conn); ++enum ct_timeout other_conn_timeout_get(const struct conn *conn); + #endif /* conntrack-private.h */ +diff --git a/openvswitch-2.14.2/lib/conntrack-tcp.c b/openvswitch-2.14.2/lib/conntrack-tcp.c +index 18a2aa7..56334e0 100644 +--- a/openvswitch-2.14.2/lib/conntrack-tcp.c ++++ b/openvswitch-2.14.2/lib/conntrack-tcp.c +@@ -160,6 +160,43 @@ tcp_bypass_seq_chk(struct conntrack *ct) + return false; + } + ++static void ++tcp_conn_update_status(struct conntrack *ct, struct conn_tcp *conn, ++ bool reply, uint16_t tcp_flags, long long now) ++{ ++ struct tcp_peer *src = &conn->peer[reply ? 1 : 0]; ++ struct tcp_peer *dst = &conn->peer[reply ? 0 : 1]; ++ ++ if (tcp_flags & TCP_SYN && src->state < CT_DPIF_TCPS_SYN_SENT) { ++ src->state = CT_DPIF_TCPS_SYN_SENT; ++ } ++ if (tcp_flags & TCP_FIN && src->state < CT_DPIF_TCPS_CLOSING) { ++ src->state = CT_DPIF_TCPS_CLOSING; ++ } ++ if (tcp_flags & TCP_ACK) { ++ if (dst->state == CT_DPIF_TCPS_SYN_SENT) { ++ dst->state = CT_DPIF_TCPS_ESTABLISHED; ++ } else if (dst->state == CT_DPIF_TCPS_CLOSING) { ++ dst->state = CT_DPIF_TCPS_FIN_WAIT_2; ++ } ++ } ++ if (tcp_flags & TCP_RST) { ++ src->state = dst->state = CT_DPIF_TCPS_TIME_WAIT; ++ } ++ ++ if (src->state >= CT_DPIF_TCPS_FIN_WAIT_2 && dst->state >= CT_DPIF_TCPS_FIN_WAIT_2) { ++ conn_update_expiration(ct, &conn->up, CT_TM_TCP_CLOSED, now); ++ } else if (src->state >= CT_DPIF_TCPS_CLOSING && dst->state >= CT_DPIF_TCPS_CLOSING) { ++ conn_update_expiration(ct, &conn->up, CT_TM_TCP_FIN_WAIT, now); ++ } else if (src->state < CT_DPIF_TCPS_ESTABLISHED || dst->state < CT_DPIF_TCPS_ESTABLISHED) { ++ conn_update_expiration(ct, &conn->up, CT_TM_TCP_OPENING, now); ++ } else if (src->state >= CT_DPIF_TCPS_CLOSING || dst->state >= CT_DPIF_TCPS_CLOSING) { ++ conn_update_expiration(ct, &conn->up, CT_TM_TCP_CLOSING, now); ++ } else { ++ conn_update_expiration(ct, &conn->up, CT_TM_TCP_ESTABLISHED, now); ++ } ++} ++ + static enum ct_update_res + tcp_conn_update(struct conntrack *ct, struct conn *conn_, + struct dp_packet *pkt, bool reply, long long now) +@@ -408,8 +445,12 @@ tcp_conn_update(struct conntrack *ct, struct conn *conn_, + src->state = dst->state = CT_DPIF_TCPS_TIME_WAIT; + } + } else { ++#ifdef HAVE_HWOFF_AGENT ++ tcp_conn_update_status(ct, conn, reply, tcp_flags, now); ++#else + COVERAGE_INC(conntrack_tcp_seq_chk_failed); + return CT_UPDATE_INVALID; ++#endif + } + + return CT_UPDATE_VALID; +@@ -518,3 +559,25 @@ struct ct_l4_proto ct_proto_tcp = { + .conn_update = tcp_conn_update, + .conn_get_protoinfo = tcp_conn_get_protoinfo, + }; ++ ++enum ct_timeout tcp_conn_timeout_get(const struct conn *conn) ++{ ++ struct conn_tcp *conn_tcp = conn_tcp_cast(conn); ++ struct tcp_peer *src = &conn_tcp->peer[0]; ++ struct tcp_peer *dst = &conn_tcp->peer[1]; ++ enum ct_timeout tm; ++ ++ if (src->state >= CT_DPIF_TCPS_FIN_WAIT_2 && dst->state >= CT_DPIF_TCPS_FIN_WAIT_2) { ++ tm = CT_TM_TCP_CLOSED; ++ } else if (src->state >= CT_DPIF_TCPS_CLOSING && dst->state >= CT_DPIF_TCPS_CLOSING) { ++ tm = CT_TM_TCP_FIN_WAIT; ++ } else if (src->state < CT_DPIF_TCPS_ESTABLISHED || dst->state < CT_DPIF_TCPS_ESTABLISHED) { ++ tm = CT_TM_TCP_OPENING; ++ } else if (src->state >= CT_DPIF_TCPS_CLOSING || dst->state >= CT_DPIF_TCPS_CLOSING) { ++ tm = CT_TM_TCP_CLOSING; ++ } else { ++ tm = CT_TM_TCP_ESTABLISHED; ++ } ++ return tm; ++} ++ +diff --git a/openvswitch-2.14.2/lib/conntrack-tp.c b/openvswitch-2.14.2/lib/conntrack-tp.c +index a586d3a..ac2c48c 100644 +--- a/openvswitch-2.14.2/lib/conntrack-tp.c ++++ b/openvswitch-2.14.2/lib/conntrack-tp.c +@@ -281,6 +281,29 @@ conn_update_expiration(struct conntrack *ct, struct conn *conn, + conn_update_expiration__(ct, conn, tm, now, val); + } + ++#ifdef HAVE_HWOFF_AGENT ++/* The conn entry lock and ct_lock must be held by user */ ++void ++conn_update_expiration_no_lock(struct conntrack *ct, struct conn *conn, ++ enum ct_timeout tm, long long now) ++{ ++ struct timeout_policy *tp; ++ uint32_t val; ++ tp = timeout_policy_lookup(ct, conn->tp_id); ++ if (tp) { ++ val = tp->policy.attrs[tm_to_ct_dpif_tp(tm)]; ++ } else { ++ val = ct_dpif_netdev_tp_def[tm_to_ct_dpif_tp(tm)]; ++ } ++ ++ if (!conn->cleaned) { ++ conn->expiration = now + val * 1000; ++ ovs_list_remove(&conn->exp_node); ++ ovs_list_push_back(&ct->exp_lists[tm], &conn->exp_node); ++ } ++} ++#endif ++ + static void + conn_init_expiration__(struct conntrack *ct, struct conn *conn, + enum ct_timeout tm, long long now, +diff --git a/openvswitch-2.14.2/lib/conntrack-tp.h b/openvswitch-2.14.2/lib/conntrack-tp.h +index 4d411d1..58d9c89 100644 +--- a/openvswitch-2.14.2/lib/conntrack-tp.h ++++ b/openvswitch-2.14.2/lib/conntrack-tp.h +@@ -27,4 +27,8 @@ void conn_init_expiration(struct conntrack *ct, struct conn *conn, + enum ct_timeout tm, long long now); + void conn_update_expiration(struct conntrack *ct, struct conn *conn, + enum ct_timeout tm, long long now); ++#ifdef HAVE_HWOFF_AGENT ++void conn_update_expiration_no_lock(struct conntrack *ct, struct conn *conn, ++ enum ct_timeout tm, long long now); ++#endif + #endif +diff --git a/openvswitch-2.14.2/lib/conntrack.c b/openvswitch-2.14.2/lib/conntrack.c +index 6938dcb..9b3f743 100644 +--- a/openvswitch-2.14.2/lib/conntrack.c ++++ b/openvswitch-2.14.2/lib/conntrack.c +@@ -47,13 +47,7 @@ COVERAGE_DEFINE(conntrack_full); + COVERAGE_DEFINE(conntrack_long_cleanup); + COVERAGE_DEFINE(conntrack_l4csum_err); + +-struct conn_lookup_ctx { +- struct conn_key key; +- struct conn *conn; +- uint32_t hash; +- bool reply; +- bool icmp_related; +-}; ++ + + enum ftp_ctl_pkt { + /* Control packets with address and/or port specifiers. */ +@@ -83,10 +77,13 @@ struct zone_limit { + struct conntrack_zone_limit czl; + }; + +-static bool conn_key_extract(struct conntrack *, struct dp_packet *, +- ovs_be16 dl_type, struct conn_lookup_ctx *, +- uint16_t zone); +-static uint32_t conn_key_hash(const struct conn_key *, uint32_t basis); ++bool conn_key_extract(struct conntrack *, struct dp_packet *, ++ ovs_be16 dl_type, struct conn_lookup_ctx *, ++ uint16_t zone); ++uint32_t conn_key_hash(const struct conn_key *, uint32_t basis); ++bool conn_key_lookup(struct conntrack *ct, const struct conn_key *key, ++ uint32_t hash, long long now, struct conn **conn_out, bool *reply); ++void conn_to_ct_dpif_entry(const struct conn *conn, struct ct_dpif_entry *entry, long long now); + static void conn_key_reverse(struct conn_key *); + static bool valid_new(struct dp_packet *pkt, struct conn_key *); + static struct conn *new_conn(struct conntrack *ct, struct dp_packet *pkt, +@@ -288,6 +285,15 @@ ct_print_conn_info(const struct conn *c, const char *log_msg, + } + } + ++#ifdef HAVE_HWOFF_AGENT ++static ct_offload_class *g_ct_offload_class = NULL; ++void reg_ct_offload_class(ct_offload_class *class) ++{ ++ /* save in global var for now, because hwoff_agent is inited before conntrack is created. ++ will be used in conntrack_init */ ++ g_ct_offload_class = class; ++} ++#endif + /* Initializes the connection tracker 'ct'. The caller is responsible for + * calling 'conntrack_destroy()', when the instance is not needed anymore */ + struct conntrack * +@@ -301,7 +307,7 @@ conntrack_init(void) + hindex_init(&ct->alg_expectation_refs); + ovs_rwlock_unlock(&ct->resources_lock); + +- ovs_mutex_init_adaptive(&ct->ct_lock); ++ ovs_mutex_init_recursive(&ct->ct_lock); + ovs_mutex_lock(&ct->ct_lock); + cmap_init(&ct->conns); + for (unsigned i = 0; i < ARRAY_SIZE(ct->exp_lists); i++) { +@@ -319,7 +325,12 @@ conntrack_init(void) + latch_init(&ct->clean_thread_exit); + ct->clean_thread = ovs_thread_create("ct_clean", clean_thread_main, ct); + ct->ipf = ipf_init(); +- ++#ifdef HAVE_HWOFF_AGENT ++ ct->offload_class = g_ct_offload_class; ++ if (ct->offload_class != NULL && ct->offload_class->cleaned == 0) { ++ ct->offload_class->ovs_ct_init_notify(ct); ++ } ++#endif + return ct; + } + +@@ -432,6 +443,12 @@ static void + conn_clean_cmn(struct conntrack *ct, struct conn *conn) + OVS_REQUIRES(ct->ct_lock) + { ++#ifdef HAVE_HWOFF_AGENT ++ if (ct->offload_class && ct->offload_class->cleaned == 0) { ++ ct->offload_class->conn_state_notify(conn, CONN_STATE_DELETE); ++ } ++#endif ++ + if (conn->alg) { + expectation_clean(ct, &conn->key); + } +@@ -524,7 +541,7 @@ conntrack_destroy(struct conntrack *ct) + } + + +-static bool ++bool + conn_key_lookup(struct conntrack *ct, const struct conn_key *key, + uint32_t hash, long long now, struct conn **conn_out, + bool *reply) +@@ -1098,6 +1115,9 @@ conn_update_state(struct conntrack *ct, struct dp_packet *pkt, + switch (res) { + case CT_UPDATE_VALID: + pkt->md.ct_state |= CS_ESTABLISHED; ++#ifdef HAVE_HWOFF_AGENT ++ conn->offload_info.is_ct_established = true; ++#endif + pkt->md.ct_state &= ~CS_NEW; + if (ctx->reply) { + pkt->md.ct_state |= CS_REPLY_DIR; +@@ -1382,8 +1402,15 @@ process_one(struct conntrack *ct, struct dp_packet *pkt, + handle_alg_ctl(ct, ctx, pkt, ct_alg_ctl, conn, now, !!nat_action_info); + + set_cached_conn(nat_action_info, ctx, conn, pkt); ++ ++#ifdef HAVE_HWOFF_AGENT ++ if (ct->offload_class && ct->offload_class->cleaned == 0) { ++ ct->offload_class->complete_ct_info(conn, pkt); ++ } ++#endif + } + ++ + /* Sends the packets in '*pkt_batch' through the connection tracker 'ct'. All + * the packets must have the same 'dl_type' (IPv4 or IPv6) and should have + * the l3 and and l4 offset properly set. Performs fragment reassembly with +@@ -1478,6 +1505,21 @@ set_label(struct dp_packet *pkt, struct conn *conn, + } + + ++#ifdef HAVE_HWOFF_AGENT ++static void ++ct_update_expiration(struct conntrack *ct) ++{ ++ struct conn *conn; ++ if (!ovs_mutex_trylock(&ct->ct_lock)) { ++ CMAP_FOR_EACH(conn, cm_node, &ct->conns) { ++ if (ct->offload_class && ct->offload_class->cleaned == 0) { ++ ct->offload_class->update_conn_statistics(conn); ++ } ++ } ++ ovs_mutex_unlock(&ct->ct_lock); ++ } ++} ++#endif + /* Delete the expired connections from 'ctb', up to 'limit'. Returns the + * earliest expiration time among the remaining connections in 'ctb'. Returns + * LLONG_MAX if 'ctb' is empty. The return value might be smaller than 'now', +@@ -1563,6 +1605,9 @@ clean_thread_main(void *f_) + + while (!latch_is_set(&ct->clean_thread_exit)) { + long long next_wake; ++#ifdef HAVE_HWOFF_AGENT ++ ct_update_expiration(ct); ++#endif + long long now = time_msec(); + next_wake = conntrack_clean(ct, now); + +@@ -1657,6 +1702,65 @@ extract_l3_ipv6(struct conn_key *key, const void *data, size_t size, + return true; + } + ++#ifdef HAVE_HWOFF_AGENT ++#define TCP_LEN_SHIFT 2 ++static inline bool ++is_vaild_tcp_length(const struct tcp_header *tcp, size_t size) ++{ ++ if (size < sizeof *tcp) { ++ return false; ++ } ++ size_t tcp_len = TCP_OFFSET(tcp->tcp_ctl) << TCP_LEN_SHIFT; ++ return !(OVS_UNLIKELY(tcp_len > size || tcp_len < TCP_HEADER_LEN)); ++} ++ ++static inline bool ++check_l4_tcp(const struct conn_key *key OVS_UNUSED, const void *data, size_t size, ++ const void *l3 OVS_UNUSED, bool validate_checksum OVS_UNUSED) ++{ ++ const struct tcp_header *tcp = data; ++ if(!is_vaild_tcp_length(tcp, size)) { ++ return false; ++ } ++ ++ return true; ++} ++ ++static inline bool ++is_vaild_udp_length(const struct udp_header *udp, size_t size) ++{ ++ if (size < sizeof *udp) { ++ return false; ++ } ++ size_t udp_len = ntohs(udp->udp_len); ++ return !(OVS_UNLIKELY(udp_len > size || udp_len < UDP_HEADER_LEN)); ++} ++ ++static inline bool ++check_l4_udp(const struct conn_key *key OVS_UNUSED, const void *data, size_t size, ++ const void *l3 OVS_UNUSED, bool validate_checksum OVS_UNUSED) ++{ ++ const struct udp_header *udp = data; ++ if(!is_vaild_udp_length(udp, size)) { ++ return false; ++ } ++ return true; ++} ++ ++static inline bool ++check_l4_icmp(const void *data OVS_UNUSED, size_t size OVS_UNUSED, bool validate_checksum OVS_UNUSED) ++{ ++ return true; ++} ++ ++static inline bool ++check_l4_icmp6(const struct conn_key *key OVS_UNUSED, const void *data OVS_UNUSED, size_t size OVS_UNUSED, ++ const void *l3 OVS_UNUSED, bool validate_checksum OVS_UNUSED) ++{ ++ return true; ++} ++#else ++ + static inline bool + checksum_valid(const struct conn_key *key, const void *data, size_t size, + const void *l3) +@@ -1725,6 +1829,7 @@ check_l4_icmp6(const struct conn_key *key, const void *data, size_t size, + { + return validate_checksum ? checksum_valid(key, data, size, l3) : true; + } ++#endif + + static inline bool + extract_l4_tcp(struct conn_key *key, const void *data, size_t size, +@@ -1987,7 +2092,7 @@ extract_l4(struct conn_key *key, const void *data, size_t size, bool *related, + } + } + +-static bool ++bool + conn_key_extract(struct conntrack *ct, struct dp_packet *pkt, ovs_be16 dl_type, + struct conn_lookup_ctx *ctx, uint16_t zone) + { +@@ -2088,7 +2193,7 @@ ct_endpoint_hash_add(uint32_t hash, const struct ct_endpoint *ep) + } + + /* Symmetric */ +-static uint32_t ++uint32_t + conn_key_hash(const struct conn_key *key, uint32_t basis) + { + uint32_t hsrc, hdst, hash; +@@ -2361,7 +2466,9 @@ static struct conn * + new_conn(struct conntrack *ct, struct dp_packet *pkt, struct conn_key *key, + long long now, uint32_t tp_id) + { +- return l4_protos[key->nw_proto]->new_conn(ct, pkt, now, tp_id); ++ ++ struct conn *conn = l4_protos[key->nw_proto]->new_conn(ct, pkt, now, tp_id); ++ return conn; + } + + static void +@@ -2475,7 +2582,7 @@ tuple_to_conn_key(const struct ct_dpif_tuple *tuple, uint16_t zone, + key->zone = zone; + } + +-static void ++void + conn_to_ct_dpif_entry(const struct conn *conn, struct ct_dpif_entry *entry, + long long now) + { +@@ -2495,6 +2602,14 @@ conn_to_ct_dpif_entry(const struct conn *conn, struct ct_dpif_entry *entry, + if (class->conn_get_protoinfo) { + class->conn_get_protoinfo(conn, &entry->protoinfo); + } ++ ++#ifdef HAVE_HWOFF_AGENT ++ if (g_ct_offload_class) { ++ entry->print_offload = false; ++ g_ct_offload_class->get_ct_offload_info(conn, entry); ++ } ++#endif ++ + ovs_mutex_unlock(&conn->lock); + + entry->timeout = (expiration > 0) ? expiration / 1000 : 0; +@@ -2557,6 +2672,71 @@ conntrack_dump_done(struct conntrack_dump *dump OVS_UNUSED) + return 0; + } + ++#ifdef HAVE_HWOFF_AGENT ++static bool ++conntrack_ipv6_compare(struct in6_addr *sip1, struct in6_addr *sip2, ++ struct in6_addr *mask) ++{ ++ struct in6_addr src_ip = ipv6_addr_bitand(sip1, mask); ++ struct in6_addr dst_ip = ipv6_addr_bitand(sip2, mask); ++ return !memcmp(&src_ip, &dst_ip, sizeof(struct in6_addr)); ++} ++ ++static bool ++conntrack_ipv4_compare(ovs_be32 sip1, ovs_be32 sip2, ++ ovs_be32 mask) ++{ ++ return ((sip1 & mask) == (sip2 & mask)); ++} ++ ++static bool ++conntrack_ip_compare(union ct_addr *ip1, union ct_addr *ip2, ++ union ct_addr *mask, uint16_t dl_type, uint16_t conn_dl_type) ++{ ++ if (ip1 == NULL || ip2 == NULL || mask == NULL) { ++ return false; ++ } ++ ++ if (htons(dl_type) != conn_dl_type) { ++ return false; ++ } ++ ++ if (dl_type == ETH_TYPE_IP) { ++ return conntrack_ipv4_compare(ip1->ipv4, ip2->ipv4, mask->ipv4); ++ } ++ ++ return conntrack_ipv6_compare(&ip1->ipv6, &ip2->ipv6, &mask->ipv6); ++} ++ ++int ++conntrack_flush(struct conntrack *ct, const uint16_t *zone, ++ union ct_addr *sip, union ct_addr *dip, ++ union ct_addr *smask, union ct_addr *dmask, ++ uint16_t dl_type, bool is_force) ++{ ++ struct conn *conn; ++ ++ ovs_mutex_lock(&ct->ct_lock); ++ CMAP_FOR_EACH(conn, cm_node, &ct->conns) { ++ if ((!zone || *zone == conn->key.zone) ++ && (conn->conn_type == CT_CONN_TYPE_DEFAULT) ++ && (!sip || !smask || conntrack_ip_compare(sip, &conn->key.src.addr, ++ smask, dl_type, conn->key.dl_type)) ++ && (!dip || !dmask || conntrack_ip_compare(dip, &conn->key.dst.addr, ++ dmask, dl_type, conn->key.dl_type))) { ++ /* Special deal for elb streams, keep it */ ++ if (!is_force && conn->label.u32[0]) { ++ continue; ++ } ++ conn_clean_one(ct, conn); ++ } ++ ++ } ++ ovs_mutex_unlock(&ct->ct_lock); ++ ++ return 0; ++} ++#else + int + conntrack_flush(struct conntrack *ct, const uint16_t *zone) + { +@@ -2572,6 +2752,7 @@ conntrack_flush(struct conntrack *ct, const uint16_t *zone) + + return 0; + } ++#endif + + int + conntrack_flush_tuple(struct conntrack *ct, const struct ct_dpif_tuple *tuple, +diff --git a/openvswitch-2.14.2/lib/conntrack.h b/openvswitch-2.14.2/lib/conntrack.h +index 9553b18..7e342c5 100644 +--- a/openvswitch-2.14.2/lib/conntrack.h ++++ b/openvswitch-2.14.2/lib/conntrack.h +@@ -132,7 +132,14 @@ int conntrack_dump_start(struct conntrack *, struct conntrack_dump *, + int conntrack_dump_next(struct conntrack_dump *, struct ct_dpif_entry *); + int conntrack_dump_done(struct conntrack_dump *); + ++#ifdef HAVE_HWOFF_AGENT ++int conntrack_flush(struct conntrack *ct, const uint16_t *zone, ++ union ct_addr *sip, union ct_addr *dip, ++ union ct_addr *smask, union ct_addr *dmask, ++ uint16_t dl_type, bool is_force); ++#else + int conntrack_flush(struct conntrack *, const uint16_t *zone); ++#endif + int conntrack_flush_tuple(struct conntrack *, const struct ct_dpif_tuple *, + uint16_t zone); + int conntrack_set_maxconns(struct conntrack *ct, uint32_t maxconns); +diff --git a/openvswitch-2.14.2/lib/ct-dpif.c b/openvswitch-2.14.2/lib/ct-dpif.c +index 8c2480e..d7566c9 100644 +--- a/openvswitch-2.14.2/lib/ct-dpif.c ++++ b/openvswitch-2.14.2/lib/ct-dpif.c +@@ -118,6 +118,36 @@ ct_dpif_dump_done(struct ct_dpif_dump_state *dump) + * entries in '*zone'. + * - If 'tuple' is not NULL, flush the conntrack entry specified by 'tuple' + * in '*zone'. If 'zone' is NULL, use the default zone (zone 0). */ ++#ifdef HAVE_HWOFF_AGENT ++typedef int (*ct_flush_t)(struct dpif *, const uint16_t *zone, ++ union ct_addr *sip, union ct_addr *dip, ++ union ct_addr *smask, union ct_addr *dmask, uint16_t dl_type, bool is_force); ++static inline void ++log_ct_flush(struct dpif *dpif, const uint16_t *zone) ++{ ++ if (zone) { ++ VLOG_DBG("%s: ct_flush: %"PRIu16, dpif_name(dpif), *zone); ++ } else { ++ VLOG_DBG("%s: ct_flush: ", dpif_name(dpif)); ++ } ++ ++} ++ ++int ++ct_dpif_flush(struct dpif *dpif, const uint16_t *zone, ++ union ct_addr *sip, union ct_addr *dip, ++ union ct_addr *smask, union ct_addr *dmask, ++ uint16_t dl_type, bool is_force) ++{ ++ ct_flush_t ct_flush = NULL; ++ ++ log_ct_flush(dpif, zone); ++ ++ ct_flush = dpif->dpif_class->ct_flush; ++ ++ return ct_flush ? ct_flush(dpif, zone, sip, dip, smask, dmask, dl_type, is_force) : EOPNOTSUPP; ++} ++#else + int + ct_dpif_flush(struct dpif *dpif, const uint16_t *zone, + const struct ct_dpif_tuple *tuple) +@@ -138,6 +168,7 @@ ct_dpif_flush(struct dpif *dpif, const uint16_t *zone, + ? dpif->dpif_class->ct_flush(dpif, zone, tuple) + : EOPNOTSUPP); + } ++#endif + + int + ct_dpif_set_maxconns(struct dpif *dpif, uint32_t maxconns) +@@ -328,6 +359,11 @@ ct_dpif_format_entry(const struct ct_dpif_entry *entry, struct ds *ds, + ct_dpif_format_tuple(ds, &entry->tuple_master); + ds_put_cstr(ds, ")"); + } ++ ++ if (entry->print_offload) { ++ ds_put_format(ds, ",offloaded=(orig=%s,", entry->init_dir_offload_state); ++ ds_put_format(ds, "reply=%s)", entry->reply_dir_offload_state); ++ } + } + + void +diff --git a/openvswitch-2.14.2/lib/ct-dpif.h b/openvswitch-2.14.2/lib/ct-dpif.h +index e4c7a64..cee924c 100644 +--- a/openvswitch-2.14.2/lib/ct-dpif.h ++++ b/openvswitch-2.14.2/lib/ct-dpif.h +@@ -20,6 +20,8 @@ + #include "openvswitch/types.h" + #include "packets.h" + ++union ct_addr; ++ + union ct_dpif_inet_addr { + ovs_be32 ip; + ovs_be32 ip6[4]; +@@ -197,6 +199,9 @@ struct ct_dpif_entry { + uint32_t timeout; + uint32_t mark; + uint32_t bkt; /* CT bucket number. */ ++ bool print_offload; ++ const char *init_dir_offload_state; ++ const char *reply_dir_offload_state; + }; + + enum { +@@ -275,8 +280,15 @@ int ct_dpif_dump_start(struct dpif *, struct ct_dpif_dump_state **, + const uint16_t *zone, int *); + int ct_dpif_dump_next(struct ct_dpif_dump_state *, struct ct_dpif_entry *); + int ct_dpif_dump_done(struct ct_dpif_dump_state *); ++#ifdef HAVE_HWOFF_AGENT ++int ct_dpif_flush(struct dpif *dpif, const uint16_t *zone, ++ union ct_addr *sip, union ct_addr *dip, ++ union ct_addr *smask, union ct_addr *dmask, ++ uint16_t dl_type, bool is_force); ++#else + int ct_dpif_flush(struct dpif *, const uint16_t *zone, + const struct ct_dpif_tuple *); ++#endif + int ct_dpif_set_maxconns(struct dpif *dpif, uint32_t maxconns); + int ct_dpif_get_maxconns(struct dpif *dpif, uint32_t *maxconns); + int ct_dpif_get_nconns(struct dpif *dpif, uint32_t *nconns); +diff --git a/openvswitch-2.14.2/lib/ct_dump_extend/ct_dump_extend.c b/openvswitch-2.14.2/lib/ct_dump_extend/ct_dump_extend.c +new file mode 100644 +index 0000000..5ff5085 +--- /dev/null ++++ b/openvswitch-2.14.2/lib/ct_dump_extend/ct_dump_extend.c +@@ -0,0 +1,202 @@ ++/* ++ * Copyright (c) 2022-2022. Huawei Technologies Co., Ltd. ++ * Description: dump conntrack extend implementations ++ * ++ * 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. ++ * ++ * Create: 2022-03-24 ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "conntrack.h" ++#include "conntrack-private.h" ++#include "ct-dpif.h" ++#include "dpif-netdev.h" ++#include "openvswitch/vlog.h" ++#include "ct_dump_extend.h" ++ ++#include "ovs-atomic.h" ++ ++#define CT_DPIF_DUMP_FLOWS_MAX 3000U ++ ++enum ct_dpif_dump_status { ++ CT_DPIF_DUMP_END, ++ CT_DPIF_DUMP_START, ++}; ++ ++struct ct_dpif_dump_context { ++ pthread_mutex_t mutex; ++ FILE *file; ++ char file_name[PATH_MAX]; ++ bool verbosity; ++ bool print_statistics; ++ uint16_t zone; ++ const uint16_t *pzone; ++ atomic_count dump_flag; ++ pthread_t thread_id; ++ struct ct_dpif_dump_state *dump_state; ++ struct dpif *ct_dpif; ++}; ++ ++VLOG_DEFINE_THIS_MODULE(ct_dump_extend); ++ ++static struct ct_dpif_dump_context g_ct_dpif_dump_context = { 0 }; ++ ++static int ++ct_dump_open_file(const char *file_name, struct ds *ds) ++{ ++ struct ct_dpif_dump_context *dump_context = &g_ct_dpif_dump_context; ++ ++ if (file_name[0] != '/') { ++ ds_put_format(ds, "file name \"%s\"error: Need absolute path\n", file_name); ++ return -1; ++ } ++ ++ dump_context->file = fopen(file_name, "w+"); ++ if (dump_context->file == NULL) { ++ ds_put_format(ds, "file : %s open failed.\n", file_name); ++ return -1; ++ } ++ ++ return 0; ++}; ++ ++static void hwoff_ct_dump_flow_to_file_start(void) ++{ ++ int ret = 0; ++ int count = 0; ++ int error; ++ int tot_bkts; ++ struct ct_dpif_entry entry; ++ struct ds ds = DS_EMPTY_INITIALIZER; ++ struct ct_dpif_dump_context *dump_context = &g_ct_dpif_dump_context; ++ ++ ret = ct_dpif_dump_start(dump_context->ct_dpif, &dump_context->dump_state, ++ dump_context->pzone, &tot_bkts); ++ if (ret != 0) { ++ VLOG_ERR("ct dpif dump flow start filed ! ret = %d", ret); ++ goto start_err; ++ } ++ ret = ct_dump_open_file(dump_context->file_name, &ds); ++ if (ret != 0) { ++ goto open_err; ++ } ++ ++ while (!(error = ct_dpif_dump_next(dump_context->dump_state, &entry))) { ++ count++; ++ ct_dpif_format_entry(&entry, &ds, dump_context->verbosity, ++ dump_context->print_statistics); ++ ct_dpif_entry_uninit(&entry); ++ ds_put_format(&ds, "\n"); ++ if (count >= CT_DPIF_DUMP_FLOWS_MAX) { ++ ret = fwrite(ds_cstr(&ds), strlen(ds_cstr(&ds)), 1, dump_context->file); ++ if (ret != 1 && ferror(dump_context->file) != 0) { ++ VLOG_ERR("ct flow write to file failed ! ret = %d\n", ret); ++ break; ++ } ++ count = 0; ++ ds_clear(&ds); ++ } ++ } ++ /* Any CT entry was dumped with no issue. */ ++ if (error == EOF) { ++ VLOG_INFO("ct flow dump to file done.\n"); ++ ret = fwrite(ds_cstr(&ds), strlen(ds_cstr(&ds)), 1, dump_context->file); ++ if (ret != 1 && ferror(dump_context->file) != 0) { ++ VLOG_ERR("ct flow write to file failed ! ret = %d\n", ret); ++ } ++ } else if (error) { ++ VLOG_ERR("ct flow dump to file failed ! error = %d\n", error); ++ } ++ ++ fclose(dump_context->file); ++ dump_context->file = NULL; ++open_err: ++ ds_destroy(&ds); ++ ct_dpif_dump_done(dump_context->dump_state); ++ dump_context->dump_state = NULL; ++start_err: ++ dpif_close(dump_context->ct_dpif); ++ dump_context->ct_dpif = NULL; ++ atomic_count_set(&dump_context->dump_flag, CT_DPIF_DUMP_END); ++} ++ ++int ++hwoff_dpctl_dump_write(struct ct_dump_extend *dump) ++{ ++ struct ct_dpif_dump_context *dump_context = &g_ct_dpif_dump_context; ++ char *resolve_path = NULL; ++ char *result = NULL; ++ ++ if (strnlen(dump->file_name, PATH_MAX) == 0) { ++ ds_put_format(dump->ds, "file name length is 0\n"); ++ return -1; ++ } ++ if (strnlen(dump->file_name, PATH_MAX) >= PATH_MAX) { ++ ds_put_format(dump->ds, "file name too long, length need less than %d, actually %zu\n", ++ PATH_MAX, strnlen(dump->file_name, PATH_MAX)); ++ return -1; ++ } ++ ++ resolve_path = (char *)malloc(PATH_MAX); ++ if (!resolve_path) { ++ ds_put_format(dump->ds, "malloc memmory for ct file real path failed\n"); ++ return -1; ++ } ++ memset(resolve_path, 0, PATH_MAX); ++ result = realpath(dump->file_name, resolve_path); ++ (void)result; ++ ++ if ((strlen(resolve_path) == 0) || (strlen(resolve_path) >= PATH_MAX)) { ++ ds_put_format(dump->ds, "resolve path %s is invalid\n", resolve_path); ++ free(resolve_path); ++ return -1; ++ } ++ ++ if (strcmp(resolve_path, dump->file_name) != 0) { ++ ds_put_format(dump->ds, "file path is incorrect, please check the file path\n"); ++ free(resolve_path); ++ return -1; ++ } ++ ++ memset(dump_context->file_name, 0, PATH_MAX); ++ strncpy(dump_context->file_name, resolve_path, PATH_MAX - 1); ++ free(resolve_path); ++ ++ dump_context->verbosity = dump->verbosity; ++ dump_context->print_statistics = dump->print_statistics; ++ dump_context->ct_dpif = dump->ct_dpif; ++ if (dump->pzone != NULL) { ++ dump_context->zone = *dump->pzone; ++ dump_context->pzone = &dump_context->zone; ++ } ++ ++ pthread_mutex_lock(&dump_context->mutex); ++ if (CT_DPIF_DUMP_END != atomic_count_get(&dump_context->dump_flag)) { ++ ds_put_format(dump->ds, "A threads for dumping CT flows is running. Try again later. thread id = %lu\n", ++ dump_context->thread_id); ++ pthread_mutex_unlock(&dump_context->mutex); ++ return -1; ++ } ++ atomic_count_set(&dump_context->dump_flag, CT_DPIF_DUMP_START); ++ pthread_mutex_unlock(&dump_context->mutex); ++ ++ ds_put_format(dump->ds, "starting dump ct flow to file: %s.\n", dump->file_name); ++ dump_context->thread_id = ovs_thread_create("hwoff_dump_ct_flow_to_file", (void *)hwoff_ct_dump_flow_to_file_start, NULL); ++ VLOG_INFO("dump ct flow to file thread id = %lu", dump_context->thread_id); ++ return 0; ++} +diff --git a/openvswitch-2.14.2/lib/ct_dump_extend/ct_dump_extend.h b/openvswitch-2.14.2/lib/ct_dump_extend/ct_dump_extend.h +new file mode 100644 +index 0000000..1551573 +--- /dev/null ++++ b/openvswitch-2.14.2/lib/ct_dump_extend/ct_dump_extend.h +@@ -0,0 +1,34 @@ ++/* ++ * Copyright (c) 2022-2022. Huawei Technologies Co., Ltd. ++ * Description: dump conntrack extend definitions ++ * ++ * 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. ++ * ++ * Create: 2022-03-24 ++ */ ++ ++#ifndef CT_DUMP_EXTEND_CT_H ++#define CT_DUMP_EXTEND_CT_H ++ ++struct ct_dump_extend { ++ bool verbosity; ++ bool print_statistics; ++ const char *file_name; ++ const uint16_t *pzone; ++ struct ds *ds; ++ struct dpif *ct_dpif; ++}; ++ ++int hwoff_dpctl_dump_write(struct ct_dump_extend *dump); ++ ++#endif +diff --git a/openvswitch-2.14.2/lib/ct_offload_provider.h b/openvswitch-2.14.2/lib/ct_offload_provider.h +new file mode 100644 +index 0000000..fb37bc8 +--- /dev/null ++++ b/openvswitch-2.14.2/lib/ct_offload_provider.h +@@ -0,0 +1,49 @@ ++/* ++ * Copyright (c) 2021-2022. Huawei Technologies Co., Ltd. ++ * Description: ct_offload_privider ++ * ++ * 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. ++ * ++ * Create: 2021-07-09 ++ */ ++#ifndef CT_OFFLOAD_PROVIDER_H ++#define CT_OFFLOAD_PROVIDER_H ++ ++#include "openvswitch/types.h" ++#include "dp-packet.h" ++ ++#ifdef HAVE_HWOFF_AGENT ++#define INVALID_CONN_ID 0 ++ ++typedef uint32_t conn_id; ++struct conn; ++struct conntrack; ++struct ct_dpif_entry; ++ ++enum { ++ CONN_STATE_DELETE, ++}; ++ ++typedef struct { ++ int (*ovs_ct_init_notify)(struct conntrack *ovs_ct); ++ int (*complete_ct_info)(struct conn *conn, struct dp_packet *packet); ++ int (*conn_state_notify)(struct conn *conn, int new_state); ++ int (*get_ct_offload_info)(const struct conn *conn, struct ct_dpif_entry *entry); ++ void (*update_conn_statistics)(struct conn *conn); ++ volatile uint16_t cleaned; ++ volatile uint16_t reserved[3]; ++} ct_offload_class; ++ ++#endif // HAVE_HWOFF_AGENT ++ ++#endif +\ No newline at end of file +diff --git a/openvswitch-2.14.2/lib/dp-packet.h b/openvswitch-2.14.2/lib/dp-packet.h +index 9e2d06b..9dd7421 100644 +--- a/openvswitch-2.14.2/lib/dp-packet.h ++++ b/openvswitch-2.14.2/lib/dp-packet.h +@@ -55,6 +55,7 @@ enum OVS_PACKED_ENUM dp_packet_source { + #endif + + /* Bit masks for the 'ol_flags' member of the 'dp_packet' structure. */ ++#ifdef DPDK_2011_AND_BEFORE + enum dp_packet_offload_mask { + /* Value 0 is not used. */ + /* Is the 'rss_hash' valid? */ +@@ -83,6 +84,36 @@ enum dp_packet_offload_mask { + DEF_OL_FLAG(DP_PACKET_OL_TX_SCTP_CKSUM, PKT_TX_SCTP_CKSUM, 0x800), + /* Adding new field requires adding to DP_PACKET_OL_SUPPORTED_MASK. */ + }; ++#else ++enum dp_packet_offload_mask { ++ /* Value 0 is not used. */ ++ /* Is the 'rss_hash' valid? */ ++ DEF_OL_FLAG(DP_PACKET_OL_RSS_HASH, RTE_MBUF_F_RX_RSS_HASH, 0x1), ++ /* Is the 'flow_mark' valid? */ ++ DEF_OL_FLAG(DP_PACKET_OL_FLOW_MARK, RTE_MBUF_F_RX_FDIR_ID, 0x2), ++ /* Bad L4 checksum in the packet. */ ++ DEF_OL_FLAG(DP_PACKET_OL_RX_L4_CKSUM_BAD, RTE_MBUF_F_RX_L4_CKSUM_BAD, 0x4), ++ /* Bad IP checksum in the packet. */ ++ DEF_OL_FLAG(DP_PACKET_OL_RX_IP_CKSUM_BAD, RTE_MBUF_F_RX_IP_CKSUM_BAD, 0x8), ++ /* Valid L4 checksum in the packet. */ ++ DEF_OL_FLAG(DP_PACKET_OL_RX_L4_CKSUM_GOOD, RTE_MBUF_F_RX_L4_CKSUM_GOOD, 0x10), ++ /* Valid IP checksum in the packet. */ ++ DEF_OL_FLAG(DP_PACKET_OL_RX_IP_CKSUM_GOOD, RTE_MBUF_F_RX_IP_CKSUM_GOOD, 0x20), ++ /* TCP Segmentation Offload. */ ++ DEF_OL_FLAG(DP_PACKET_OL_TX_TCP_SEG, RTE_MBUF_F_TX_TCP_SEG, 0x40), ++ /* Offloaded packet is IPv4. */ ++ DEF_OL_FLAG(DP_PACKET_OL_TX_IPV4, RTE_MBUF_F_TX_IPV4, 0x80), ++ /* Offloaded packet is IPv6. */ ++ DEF_OL_FLAG(DP_PACKET_OL_TX_IPV6, RTE_MBUF_F_TX_IPV6, 0x100), ++ /* Offload TCP checksum. */ ++ DEF_OL_FLAG(DP_PACKET_OL_TX_TCP_CKSUM, RTE_MBUF_F_TX_TCP_CKSUM, 0x200), ++ /* Offload UDP checksum. */ ++ DEF_OL_FLAG(DP_PACKET_OL_TX_UDP_CKSUM, RTE_MBUF_F_TX_UDP_CKSUM, 0x400), ++ /* Offload SCTP checksum. */ ++ DEF_OL_FLAG(DP_PACKET_OL_TX_SCTP_CKSUM, RTE_MBUF_F_TX_SCTP_CKSUM, 0x800), ++ /* Adding new field requires adding to DP_PACKET_OL_SUPPORTED_MASK. */ ++}; ++#endif + + #define DP_PACKET_OL_SUPPORTED_MASK (DP_PACKET_OL_RSS_HASH | \ + DP_PACKET_OL_FLOW_MARK | \ +@@ -558,6 +589,36 @@ dp_packet_set_base(struct dp_packet *b, void *d) + b->mbuf.buf_addr = d; + } + ++#ifdef HAVE_HWOFF_AGENT ++static inline uint32_t ++dp_packet_size(const struct dp_packet *b) ++{ ++ return rte_pktmbuf_pkt_len(&(b->mbuf)); ++} ++ ++static inline void ++hwoff_nonlinear_pkt_set_size(struct dp_packet *b, uint32_t v) ++{ ++ int pack_len_diff = v - b->mbuf.pkt_len; ++ b->mbuf.data_len += pack_len_diff; ++ ++ rte_pktmbuf_pkt_len(&(b->mbuf)) = v; ++} ++ ++static inline void ++dp_packet_set_size(struct dp_packet *b, uint32_t v) ++{ ++ if (b->mbuf.nb_segs <= 1) { ++ rte_pktmbuf_pkt_len(&(b->mbuf)) = v; ++ rte_pktmbuf_data_len(&(b->mbuf)) = v; ++ return; ++ } ++ ++ hwoff_nonlinear_pkt_set_size(b, v); ++} ++ ++#else ++ + static inline uint32_t + dp_packet_size(const struct dp_packet *b) + { +@@ -579,6 +640,7 @@ dp_packet_set_size(struct dp_packet *b, uint32_t v) + b->mbuf.pkt_len = v; /* Total length of all segments linked to + * this segment. */ + } ++#endif + + static inline uint16_t + __packet_data(const struct dp_packet *b) +diff --git a/openvswitch-2.14.2/lib/dpctl.c b/openvswitch-2.14.2/lib/dpctl.c +index b232d43..3ab83c8 100644 +--- a/openvswitch-2.14.2/lib/dpctl.c ++++ b/openvswitch-2.14.2/lib/dpctl.c +@@ -51,6 +51,10 @@ + #include "util.h" + #include "openvswitch/ofp-flow.h" + #include "openvswitch/ofp-port.h" ++#ifdef HAVE_HWOFF_AGENT ++#include "conntrack.h" ++#include "ct_dump_extend/ct_dump_extend.h" ++#endif + + typedef int dpctl_command_handler(int argc, const char *argv[], + struct dpctl_params *); +@@ -811,6 +815,11 @@ format_dpif_flow(struct ds *ds, const struct dpif_flow *f, struct hmap *ports, + if (dpctl_p->verbosity && f->ufid_present) { + odp_format_ufid(&f->ufid, ds); + ds_put_cstr(ds, ", "); ++#ifdef HAVE_HWOFF_AGENT ++ odp_format_mega_ufid(&f->mega_ufid, ds); ++ ds_put_cstr(ds, ", "); ++#endif ++ + } + odp_flow_format(f->key, f->key_len, f->mask, f->mask_len, ports, ds, + dpctl_p->verbosity); +@@ -1421,6 +1430,16 @@ dpctl_dump_conntrack(int argc, const char *argv[], + int tot_bkts; + struct dpif *dpif; + int error; ++#ifdef HAVE_HWOFF_AGENT ++ bool is_need_dump_to_file = false; ++ const char *dump_file_name = NULL; ++ ++ if (argc >= 2 && (strncmp(argv[argc - 2], "-w", sizeof("-w")) == 0)) { ++ is_need_dump_to_file = true; ++ dump_file_name = argv[argc - 1]; ++ argc -= 2; ++ } ++#endif + + if (argc > 1 && ovs_scan(argv[argc - 1], "zone=%"SCNu16, &zone)) { + pzone = &zone; +@@ -1432,6 +1451,30 @@ dpctl_dump_conntrack(int argc, const char *argv[], + return error; + } + ++#ifdef HAVE_HWOFF_AGENT ++ if (is_need_dump_to_file) { ++ struct ds ds = DS_EMPTY_INITIALIZER; ++ struct ct_dump_extend ct_dump = { ++ .file_name = dump_file_name, ++ .verbosity = dpctl_p->verbosity, ++ .print_statistics = dpctl_p->print_statistics, ++ .ct_dpif = dpif, ++ .pzone = pzone, ++ .ds = &ds, ++ }; ++ error = hwoff_dpctl_dump_write(&ct_dump); ++ if (error) { ++ dpctl_error(dpctl_p, error, ds_cstr(&ds)); ++ ds_destroy(&ds); ++ dpif_close(dpif); ++ return error; ++ } ++ dpctl_print(dpctl_p, "%s\n", ds_cstr(&ds)); ++ ds_destroy(&ds); ++ return error; ++ } ++#endif ++ + error = ct_dpif_dump_start(dpif, &dump, pzone, &tot_bkts); + if (error) { + dpctl_error(dpctl_p, error, "starting conntrack dump"); +@@ -1461,6 +1504,188 @@ dpctl_dump_conntrack(int argc, const char *argv[], + return error; + } + ++#ifdef HAVE_HWOFF_AGENT ++static int conntrack_parse_ipv4_mask_len(char *str, union ct_addr *ip, union ct_addr *mask) ++{ ++ /* Mask has the same format with ip, %d prefix */ ++ char *ip_str = NULL; ++ char *mask_str = NULL; ++ uint8_t prefix; ++ ++ if (str == NULL || ip == NULL || mask == NULL) { ++ return -EINVAL; ++ } ++ ++ ip_str = strtok_r(str, "/", &mask_str); ++ if (ip_str != NULL && mask_str != NULL && (*mask_str != '\0')) { ++ if (1 == inet_pton(AF_INET, ip_str, &ip->ipv4) && ++ 1 == sscanf(mask_str, "%"SCNu8, &prefix) && prefix <= IP_MAX_MASK_LEN) { ++ mask->ipv4 = be32_prefix_mask(prefix); ++ /* restore the original str */ ++ *(mask_str - 1) = '/'; ++ return 0; ++ } ++ ++ /* restore the original str */ ++ *(mask_str - 1) = '/'; ++ } else if (ip_str != NULL && inet_pton(AF_INET, ip_str, ip) == 1) { ++ mask->ipv4 = be32_prefix_mask(IP_MAX_MASK_LEN); ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int conntrack_parse_ipv6_mask_len(char *str, union ct_addr *ip, union ct_addr *mask) ++{ ++ /* Mask has the same format with ip, %d prefix */ ++ char *ip_str = NULL; ++ char *mask_str = NULL; ++ uint8_t prefix; ++ ++ if (str == NULL || ip == NULL || mask == NULL) { ++ return -EINVAL; ++ } ++ ++ ip_str = strtok_r(str, "/", &mask_str); ++ if (ip_str != NULL && mask_str != NULL && (*mask_str != '\0')) { ++ if (1 == inet_pton(AF_INET6, ip_str, &ip->ipv6) && ++ 1 == sscanf(mask_str, "%"SCNu8, &prefix) && prefix <= IPV6_MAX_MASK_LEN) { ++ mask->ipv6 = ipv6_create_mask(prefix); ++ /* restore the orignal str */ ++ *(mask_str - 1) = '/'; ++ return 0; ++ } ++ /* restore the original str */ ++ *(mask_str - 1) = '/'; ++ } else if (ip_str != NULL && inet_pton(AF_INET6, ip_str, &ip->ipv6) == 1) { ++ (void)memset(mask, sizeof(*mask), 0xff); ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int conntrack_flush_get_ip(char *ip_str, union ct_addr *ip, ++ union ct_addr *mask, bool *is_ipv6) ++{ ++ if (conntrack_parse_ipv4_mask_len(ip_str, ip, mask) == 0) { ++ *is_ipv6 = false; ++ return 0; ++ } ++ ++ if (conntrack_parse_ipv6_mask_len(ip_str, ip, mask) == 0) { ++ *is_ipv6 = true; ++ return 0; ++ } ++ ++ return -1; ++} ++ ++static int ++dpctl_flush_conntrack(int argc, const char *argv[], ++ struct dpctl_params *dpctl_p) ++{ ++ int error; ++ uint16_t dl_type; ++ char *ip = NULL; ++ int args = argc - 1; ++ bool is_force = false; ++ struct dpif *dpif = NULL; ++ uint16_t zone, *pzone = NULL; ++ union ct_addr addr_src; ++ union ct_addr mask_src; ++ union ct_addr addr_dst; ++ union ct_addr mask_dst; ++ bool d_addr_ipv4_flag = false; ++ bool d_addr_ipv6_flag = false; ++ bool s_addr_ipv4_flag = false; ++ bool s_addr_ipv6_flag = false; ++ struct ds ds = DS_EMPTY_INITIALIZER; ++ ++ memset(&addr_src, 0, sizeof(union ct_addr)); ++ memset(&addr_dst, 0, sizeof(union ct_addr)); ++ ++ if (args && !strncmp(argv[args], "--force", sizeof("--force") - 1)) { ++ is_force = true; ++ args--; ++ } ++ ++ if (args && !strncmp(argv[args], "dip=", sizeof("dip") - 1)) { ++ ip = xstrdup(argv[args] + (sizeof("dip=") - 1)); ++ error = conntrack_flush_get_ip(ip, &addr_dst, &mask_dst, &d_addr_ipv6_flag); ++ free(ip); ++ if (error) { ++ dpctl_error(dpctl_p, error, "parse dip error."); ++ return error; ++ } ++ d_addr_ipv4_flag = !d_addr_ipv6_flag; ++ args--; ++ } ++ ++ if (args && !strncmp(argv[args], "sip=", sizeof("sip=") - 1)) { ++ ip = xstrdup(argv[args] + (sizeof("sip=") - 1)); ++ error = conntrack_flush_get_ip(ip, &addr_src, &mask_src, &s_addr_ipv6_flag); ++ free(ip); ++ if (error) { ++ dpctl_error(dpctl_p, error, "parse sip error."); ++ return error; ++ } ++ s_addr_ipv4_flag = !s_addr_ipv6_flag; ++ args--; ++ } ++ ++ if ((s_addr_ipv6_flag && d_addr_ipv4_flag) || ++ (s_addr_ipv4_flag && d_addr_ipv6_flag)) { ++ dpctl_error(dpctl_p, EINVAL, "sip/dip mismatch."); ++ return EINVAL; ++ } ++ ++ if (d_addr_ipv6_flag || s_addr_ipv6_flag) { ++ dl_type = ETH_TYPE_IPV6; ++ } else if (d_addr_ipv4_flag || s_addr_ipv4_flag) { ++ dl_type = ETH_TYPE_IP; ++ } else { ++ dl_type = 0; ++ } ++ ++ /* Parse zone */ ++ if (args && ovs_scan(argv[args], "zone=%"SCNu16, &zone)) { ++ pzone = &zone; ++ args--; ++ } ++ ++ /* Report error if there are more than one unparsed argument */ ++ if (args > 1) { ++ ds_put_cstr(&ds, "invalid argument"); ++ error = EINVAL; ++ goto error; ++ } ++ ++ error = opt_dpif_open(argc, argv, dpctl_p, 6, &dpif); ++ if (error) { ++ return error; ++ } ++ ++ error = ct_dpif_flush(dpif, pzone, ++ (s_addr_ipv6_flag || s_addr_ipv4_flag) ? &addr_src : 0, ++ (d_addr_ipv6_flag || d_addr_ipv4_flag) ? &addr_dst : 0, ++ (s_addr_ipv6_flag || s_addr_ipv4_flag) ? &mask_src : 0, ++ (d_addr_ipv6_flag || d_addr_ipv4_flag) ? &mask_dst : 0, ++ dl_type, is_force); ++ ++ if (!error) { ++ dpif_close(dpif); ++ return 0; ++ } ++ ++ ds_put_cstr(&ds, "failed to flush conntrack"); ++ ++error: ++ dpctl_error(dpctl_p, error, "%s", ds_cstr(&ds)); ++ ds_destroy(&ds); ++ dpif_close(dpif); ++ return error; ++} ++#else + static int + dpctl_flush_conntrack(int argc, const char *argv[], + struct dpctl_params *dpctl_p) +@@ -1510,6 +1735,7 @@ error: + dpif_close(dpif); + return error; + } ++#endif + + static int + dpctl_ct_stats_show(int argc, const char *argv[], +@@ -2529,10 +2755,17 @@ static const struct dpctl_command all_commands[] = { + { "get-flow", "[dp] ufid", 1, 2, dpctl_get_flow, DP_RO }, + { "del-flow", "[dp] flow", 1, 2, dpctl_del_flow, DP_RW }, + { "del-flows", "[dp]", 0, 1, dpctl_del_flows, DP_RW }, ++#ifdef HAVE_HWOFF_AGENT ++ { "dump-conntrack", "[-m] [-s] [dp] [zone=N] [-w file]", ++ 0, 4, dpctl_dump_conntrack, DP_RO }, ++ { "flush-conntrack", "[dp] [zone=N] [sip=x.x.x.x/x] [dip=x.x.x.x/x] [--force]", ++ 0, 5, dpctl_flush_conntrack, DP_RW }, ++#else + { "dump-conntrack", "[-m] [-s] [dp] [zone=N]", + 0, 4, dpctl_dump_conntrack, DP_RO }, + { "flush-conntrack", "[dp] [zone=N] [ct-tuple]", 0, 3, + dpctl_flush_conntrack, DP_RW }, ++#endif + { "ct-stats-show", "[dp] [zone=N]", + 0, 3, dpctl_ct_stats_show, DP_RO }, + { "ct-bkts", "[dp] [gt=N]", 0, 2, dpctl_ct_bkts, DP_RO }, +@@ -2675,6 +2908,8 @@ dpctl_unixctl_handler(struct unixctl_conn *conn, int argc, const char *argv[], + case 's': + dpctl_p.print_statistics = true; + break; ++ case 'w': ++ break; + default: + ds_put_format(&ds, "Unrecognized option -%c", *opt); + error = true; +diff --git a/openvswitch-2.14.2/lib/dpdk.c b/openvswitch-2.14.2/lib/dpdk.c +index 2f235a7..df849e3 100644 +--- a/openvswitch-2.14.2/lib/dpdk.c ++++ b/openvswitch-2.14.2/lib/dpdk.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -40,6 +41,17 @@ + #include "unixctl.h" + #include "util.h" + #include "vswitch-idl.h" ++#ifdef HAVE_HWOFF_AGENT ++#include ++#include ++#include "hwoff_init_func.h" ++ ++#define HWOFF_DPDK_HUGEPAGES_PREFIX "rte_dpak_" ++#define HWOFF_DPDK_HUGEPAGES_PATH "/dev/hugepages" ++#define HWOFF_DPDK_RUN_FILE "/var/run/dpdk" ++#define HWOFF_FUNCTION_FLUSH_PF "1" ++#define HWOFF_FUNCTION_FLUSH "/proc/hwoff_function_flush" ++#endif + + VLOG_DEFINE_THIS_MODULE(dpdk); + +@@ -339,6 +351,7 @@ dpdk_unixctl_log_set(struct unixctl_conn *conn, int argc, const char *argv[], + } + + level = dpdk_parse_log_level(level_string); ++ + if (level == -1) { + err_msg = xasprintf("invalid log level: '%s'", level_string); + } else if (rte_log_set_level_pattern(pattern, level) < 0) { +@@ -356,6 +369,104 @@ dpdk_unixctl_log_set(struct unixctl_conn *conn, int argc, const char *argv[], + unixctl_command_reply(conn, NULL); + } + ++#ifdef HAVE_HWOFF_AGENT ++static void ++hwoff_remove_fbarray(const char *path) ++{ ++ char barray_path[PATH_MAX] = {0}; ++ DIR *dir_barray = NULL; ++ struct dirent *barray_dirent = NULL; ++ ++ dir_barray = opendir(path); ++ while ((barray_dirent = readdir(dir_barray)) != NULL) { ++ if (strcmp(barray_dirent->d_name, ".") == 0) { ++ continue; ++ } ++ if (strcmp(barray_dirent->d_name, "..") == 0) { ++ continue; ++ } ++ sprintf(barray_path, "%s/%s", path, barray_dirent->d_name); ++ ++ remove(barray_path); ++ memset(barray_path, 0, sizeof(barray_path)); ++ } ++ closedir(dir_barray); ++ rmdir(path); ++} ++ ++void hwoff_clear_pf_access_hugepages(void) ++{ ++ int flr_file = -1; ++ ++ flr_file = open(HWOFF_FUNCTION_FLUSH, O_WRONLY); ++ if (flr_file < 0) { ++ VLOG_ERR("failed to open file %s with fd %d error %d", ++ HWOFF_FUNCTION_FLUSH, flr_file, errno); ++ return; ++ } ++ if (write(flr_file, HWOFF_FUNCTION_FLUSH_PF, ++ sizeof(HWOFF_FUNCTION_FLUSH_PF)) <= 0) { ++ VLOG_ERR("failed to write to %s", HWOFF_FUNCTION_FLUSH); ++ goto OUT; ++ } ++ VLOG_INFO("PF upall huge_pages flush success"); ++OUT: ++ close(flr_file); ++} ++ ++void ++hwoff_free_hugepages(void) ++{ ++ char path[PATH_MAX] = {0}; ++ char hugepages_path[PATH_MAX] = HWOFF_DPDK_HUGEPAGES_PATH; ++ char run_path[PATH_MAX] = HWOFF_DPDK_RUN_FILE; ++ DIR *dir = NULL; ++ DIR *hugepages_dir = NULL; ++ struct dirent *dir_item = NULL; ++ struct dirent *rte_dirent = NULL; ++ char prefix[] = HWOFF_DPDK_HUGEPAGES_PREFIX; ++ ++ /* unlink hugepages file */ ++ hugepages_dir = opendir(hugepages_path); ++ if (hugepages_dir) { ++ while ((dir_item = readdir(hugepages_dir)) != NULL) { ++ if (strncmp(dir_item->d_name, prefix, strlen(prefix)) == 0) { ++ sprintf(path, "%s/%s", hugepages_path, dir_item->d_name); ++ ++ unlink(path); ++ memset(path, 0, sizeof(path)); ++ } ++ } ++ closedir(hugepages_dir); ++ } ++ ++ /* remove dpdk run file */ ++ dir = opendir(run_path); ++ if (dir) { ++ while ((rte_dirent = readdir(dir)) != NULL) { ++ if (strncmp(rte_dirent->d_name, prefix, strlen(prefix)) == 0) { ++ sprintf(path, "%s/%s", run_path, rte_dirent->d_name); ++ ++ hwoff_remove_fbarray(path); ++ memset(path, 0, sizeof(path)); ++ } ++ } ++ closedir(dir); ++ } ++} ++ ++static void ++hwoff_hugepages_pre_process(struct svec *svec) ++{ ++ char ovs_prefix[64] = {0}; ++ hwoff_clear_pf_access_hugepages(); ++ hwoff_free_hugepages(); ++ ++ sprintf(ovs_prefix, "--file-prefix=%s%d", HWOFF_DPDK_HUGEPAGES_PREFIX, getpid()); ++ svec_add(svec, ovs_prefix); ++} ++#endif ++ + static bool + dpdk_init__(const struct smap *ovs_other_config) + { +@@ -481,6 +592,10 @@ dpdk_init__(const struct smap *ovs_other_config) + free(joined_args); + } + ++#ifdef HAVE_HWOFF_AGENT ++ hwoff_hugepages_pre_process(&args); ++#endif ++ + /* Copy because 'rte_eal_init' will change the argv, i.e. it will remove + * some arguments from it. '+1' to copy the terminating NULL. */ + argv = xmemdup(args.names, (args.n + 1) * sizeof args.names[0]); +diff --git a/openvswitch-2.14.2/lib/dpdk.h b/openvswitch-2.14.2/lib/dpdk.h +index 445a51d..3c08d56 100644 +--- a/openvswitch-2.14.2/lib/dpdk.h ++++ b/openvswitch-2.14.2/lib/dpdk.h +@@ -45,5 +45,9 @@ bool dpdk_available(void); + void print_dpdk_version(void); + void dpdk_status(const struct ovsrec_open_vswitch *); + bool dpdk_get_cpu_has_isa(const char *arch, const char *feature); ++#ifdef HAVE_HWOFF_AGENT ++void hwoff_free_hugepages(void); ++void hwoff_clear_pf_access_hugepages(void); ++#endif + + #endif /* dpdk.h */ +diff --git a/openvswitch-2.14.2/lib/dpif-netdev.c b/openvswitch-2.14.2/lib/dpif-netdev.c +index 02df8f1..8e0911c 100644 +--- a/openvswitch-2.14.2/lib/dpif-netdev.c ++++ b/openvswitch-2.14.2/lib/dpif-netdev.c +@@ -33,6 +33,11 @@ + #include + #include + ++#ifdef HAVE_HWOFF_AGENT ++#include "hwoff_init_func.h" ++#include "mac-learning.h" ++#endif ++ + #include "bitmap.h" + #include "cmap.h" + #include "conntrack.h" +@@ -387,6 +392,11 @@ struct dp_netdev { + /* Bonds. */ + struct ovs_mutex bond_mutex; /* Protects updates of 'tx_bonds'. */ + struct cmap tx_bonds; /* Contains 'struct tx_bond'. */ ++#ifdef HAVE_HWOFF_AGENT ++ /* callback when flush flows */ ++ dp_pmd_ukey_purge_callback *dp_pmd_ukey_purge_cb; ++ void *dp_pmd_ukey_purge_aux; ++#endif + }; + + static void meter_lock(const struct dp_netdev *dp, uint32_t meter_id) +@@ -498,6 +508,15 @@ struct dp_netdev_flow_attrs { + ATOMIC(const char *) dp_layer; /* DP layer the flow is handled in. */ + }; + ++#ifdef HAVE_HWOFF_AGENT ++struct packet_batch_per_flow { ++ unsigned int byte_count; ++ uint16_t tcp_flags; ++ struct dp_netdev_flow *flow; ++ struct dp_packet_batch array; ++}; ++#endif ++ + /* A flow in 'dp_netdev_pmd_thread's 'flow_table'. + * + * +@@ -921,6 +940,13 @@ static inline bool + pmd_perf_metrics_enabled(const struct dp_netdev_pmd_thread *pmd); + static void queue_netdev_flow_del(struct dp_netdev_pmd_thread *pmd, + struct dp_netdev_flow *flow); ++uint32_t flow_mark_alloc(void); ++void megaflow_to_mark_associate(const ovs_u128 *mega_ufid, uint32_t mark); ++void mark_to_flow_associate(const uint32_t mark, struct dp_netdev_flow *flow); ++int mark_to_flow_disassociate(struct dp_netdev_pmd_thread *pmd, struct dp_netdev_flow *flow); ++static void * ++dp_netdev_flow_offload_main(void *data OVS_UNUSED); ++ + + static void + emc_cache_init(struct emc_cache *flow_cache) +@@ -2261,6 +2287,25 @@ get_port_by_name(struct dp_netdev *dp, + return ENODEV; + } + ++odp_port_t ++dpif_netdev_get_odp_no_by_name(const char *devname) ++{ ++ struct dp_netdev_port *port = NULL; ++ struct dp_netdev *dp = shash_find_data(&dp_netdevs, "ovs-netdev"); ++ int error; ++ ++ if (dp == NULL) { ++ return ODPP_NONE; ++ } ++ ++ error = get_port_by_name(dp, devname, &port); ++ if (error != 0) { ++ return ODPP_NONE; ++ } ++ ++ return port->port_no; ++} ++ + /* Returns 'true' if there is a port with pmd netdev. */ + static bool + has_pmd_port(struct dp_netdev *dp) +@@ -2413,7 +2458,7 @@ static struct flow_mark flow_mark = { + .mark_to_flow = CMAP_INITIALIZER, + }; + +-static uint32_t ++uint32_t + flow_mark_alloc(void) + { + uint32_t mark; +@@ -2437,7 +2482,7 @@ flow_mark_free(uint32_t mark) + } + + /* associate megaflow with a mark, which is a 1:1 mapping */ +-static void ++void + megaflow_to_mark_associate(const ovs_u128 *mega_ufid, uint32_t mark) + { + size_t hash = dp_netdev_flow_hash(mega_ufid); +@@ -2488,7 +2533,7 @@ megaflow_to_mark_find(const ovs_u128 *mega_ufid) + } + + /* associate mark with a flow, which is 1:N mapping */ +-static void ++void + mark_to_flow_associate(const uint32_t mark, struct dp_netdev_flow *flow) + { + dp_netdev_flow_ref(flow); +@@ -2517,7 +2562,7 @@ flow_mark_has_no_ref(uint32_t mark) + return true; + } + +-static int ++int + mark_to_flow_disassociate(struct dp_netdev_pmd_thread *pmd, + struct dp_netdev_flow *flow) + { +@@ -2565,18 +2610,6 @@ mark_to_flow_disassociate(struct dp_netdev_pmd_thread *pmd, + return ret; + } + +-static void +-flow_mark_flush(struct dp_netdev_pmd_thread *pmd) +-{ +- struct dp_netdev_flow *flow; +- +- CMAP_FOR_EACH (flow, mark_node, &flow_mark.mark_to_flow) { +- if (flow->pmd_id == pmd->core_id) { +- queue_netdev_flow_del(pmd, flow); +- } +- } +-} +- + static struct dp_netdev_flow * + mark_to_flow_find(const struct dp_netdev_pmd_thread *pmd, + const uint32_t mark) +@@ -2631,6 +2664,154 @@ dp_netdev_append_flow_offload(struct dp_flow_offload_item *offload) + ovs_mutex_unlock(&dp_flow_offload.mutex); + } + ++#ifdef HAVE_HWOFF_AGENT ++/* Original offload solution use the flow_mark to identify the offload dp_netdev_flow, it works well ++ in one thread environment. In our solution we offload flow in pmd threads, and delete run in offload_thread, ++ so there are multi-theads process the flow_mark, flow_mark not working anymore. So we change our solution not ++ to process flow_mark at all. ++*/ ++int g_hwoff_offload_switch = 1; ++void dp_netdev_hwoff_switch_set(int value) ++{ ++ g_hwoff_offload_switch = value; ++} ++ ++static int ++dp_netdev_flow_offload_add(struct dp_netdev_pmd_thread *pmd, struct dp_netdev_flow *flow, struct match *match, ++ struct dp_packet_batch *pkts, const struct nlattr *actions, size_t actions_len) ++{ ++ int ret; ++ struct netdev *dev = NULL; ++ struct offload_info info; ++ odp_port_t in_port = flow->flow.in_port.odp_port; ++ const char *dpif_type_str = dpif_normalize_type(pmd->dp->class->type); ++ ++ if (g_hwoff_offload_switch != 1) { ++ return 0; ++ } ++ ++ if (flow->dead || (!pkts)) { ++ return -1; ++ } ++ ++ dev = netdev_ports_get(in_port, dpif_type_str); ++ if (!dev) { ++ return -1; ++ } ++ ++ hwoff_func* funcs = hwoff_get_funcs(); ++ if (!funcs->hwoff_is_support_offload(dev)) { ++ netdev_close(dev); ++ return -1; ++ } ++ ++ if (funcs->hwoff_is_ethdev(dev)) { ++ info.in_port_id = funcs->hwoff_get_eth_vport_id(dev); ++ info.in_port_type = HWOFF_PORT_TYPE_HIOVS; ++ } else { ++ info.in_port_id = 0xFFFFFFFF; ++ info.in_port_type = HWOFF_PORT_TYPE_OTHER; ++ } ++ info.pkts_info = pkts; ++ info.pmd_core_id = pmd->core_id; ++ info.pmd = pmd; ++ info.flow = flow; ++ info.modification = false; ++ ++ /* no need to take a port_mutex in pmd thread */ ++ ret = netdev_flow_put(dev, match, ++ CONST_CAST(struct nlattr *, actions), ++ actions_len, &flow->mega_ufid, &info, NULL); ++ netdev_close(dev); ++ if (ret) { ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int ++dp_netdev_flow_offload_del(struct dp_flow_offload_item *offload) ++{ ++ int ret; ++ struct netdev *dev = NULL; ++ hwoff_func* funcs = hwoff_get_funcs(); ++ struct dp_netdev_pmd_thread *pmd = offload->pmd; ++ struct dp_netdev_flow *flow = offload->flow; ++ odp_port_t in_port = flow->flow.in_port.odp_port; ++ const char *dpif_type_str = dpif_normalize_type(pmd->dp->class->type); ++ ++ dev = netdev_ports_get(in_port, dpif_type_str); ++ if (!dev) { ++ VLOG_DBG("device=%u is deleted when dp_netdev_flow_offload_del, mega_ufid="UUID_FMT, in_port, ++ UUID_ARGS((struct uuid *) &flow->mega_ufid)); ++ hiovs_offload_flow_api_del(NULL, &flow->mega_ufid, NULL); ++ return 0; ++ } ++ ++ if (!funcs->hwoff_is_support_offload(dev)) { ++ netdev_close(dev); ++ return 0; ++ } ++ ++ ovs_mutex_lock(&pmd->dp->port_mutex); ++ ret = netdev_flow_del(dev, &flow->mega_ufid, NULL); ++ ovs_mutex_unlock(&pmd->dp->port_mutex); ++ netdev_close(dev); ++ return ret; ++} ++ ++static int ++dp_netdev_flow_offload_put(struct dp_flow_offload_item *offload) ++{ ++ /* We do offload in pmd thread useing dp_netdev_flow_offload_add, here we just process modification. ++ We process modification just same as delete, thren packet will upcall then reoffload. ++ */ ++ bool modification = offload->op == DP_NETDEV_FLOW_OFFLOAD_OP_MOD; ++ ++ if (!modification) { ++ return 0; ++ } ++ ++ return dp_netdev_flow_offload_del(offload); ++} ++ ++static void ++queue_netdev_flow_put(struct dp_netdev_pmd_thread *pmd, ++ struct dp_netdev_flow *flow, struct match *match, ++ const struct nlattr *actions, size_t actions_len, bool is_modify) ++{ ++ int op; ++ struct dp_flow_offload_item *offload; ++ ++ if (!netdev_is_flow_api_enabled()) { ++ return; ++ } ++ ++ if (ovsthread_once_start(&offload_thread_once)) { ++ xpthread_cond_init(&dp_flow_offload.cond, NULL); ++ ovs_thread_create("dp_netdev_flow_offload", ++ dp_netdev_flow_offload_main, NULL); ++ ovsthread_once_done(&offload_thread_once); ++ } ++ ++ op = is_modify ? DP_NETDEV_FLOW_OFFLOAD_OP_MOD : DP_NETDEV_FLOW_OFFLOAD_OP_ADD; ++ offload = dp_netdev_alloc_flow_offload(pmd, flow, op); ++ offload->match = *match; ++ offload->actions = xmalloc(actions_len); ++ memcpy(offload->actions, actions, actions_len); ++ offload->actions_len = actions_len; ++ ++ dp_netdev_append_flow_offload(offload); ++} ++ ++bool dp_netdev_flow_dead_status_get(void *flow) ++{ ++ struct dp_netdev_flow *net_flow = (struct dp_netdev_flow *)flow; ++ return net_flow->dead; ++} ++#else ++ + static int + dp_netdev_flow_offload_del(struct dp_flow_offload_item *offload) + { +@@ -2691,6 +2872,7 @@ dp_netdev_flow_offload_put(struct dp_flow_offload_item *offload) + } + } + info.flow_mark = mark; ++ info.pmd = NULL; + + port = netdev_ports_get(in_port, dpif_type_str); + if (!port || netdev_vport_is_vport_class(port->netdev_class)) { +@@ -2726,6 +2908,41 @@ err_free: + return -1; + } + ++static void ++queue_netdev_flow_put(struct dp_netdev_pmd_thread *pmd, ++ struct dp_netdev_flow *flow, struct match *match, ++ const struct nlattr *actions, size_t actions_len, bool is_modify) ++{ ++ struct dp_flow_offload_item *offload; ++ int op; ++ ++ if (!netdev_is_flow_api_enabled()) { ++ return; ++ } ++ ++ if (ovsthread_once_start(&offload_thread_once)) { ++ xpthread_cond_init(&dp_flow_offload.cond, NULL); ++ ovs_thread_create("dp_netdev_flow_offload", ++ dp_netdev_flow_offload_main, NULL); ++ ovsthread_once_done(&offload_thread_once); ++ } ++ ++ if (flow->mark != INVALID_FLOW_MARK) { ++ op = DP_NETDEV_FLOW_OFFLOAD_OP_MOD; ++ } else { ++ op = DP_NETDEV_FLOW_OFFLOAD_OP_ADD; ++ } ++ offload = dp_netdev_alloc_flow_offload(pmd, flow, op); ++ offload->match = *match; ++ offload->actions = xmalloc(actions_len); ++ memcpy(offload->actions, actions, actions_len); ++ offload->actions_len = actions_len; ++ ++ dp_netdev_append_flow_offload(offload); ++} ++ ++#endif ++ + static void * + dp_netdev_flow_offload_main(void *data OVS_UNUSED) + { +@@ -2791,39 +3008,6 @@ queue_netdev_flow_del(struct dp_netdev_pmd_thread *pmd, + dp_netdev_append_flow_offload(offload); + } + +-static void +-queue_netdev_flow_put(struct dp_netdev_pmd_thread *pmd, +- struct dp_netdev_flow *flow, struct match *match, +- const struct nlattr *actions, size_t actions_len) +-{ +- struct dp_flow_offload_item *offload; +- int op; +- +- if (!netdev_is_flow_api_enabled()) { +- return; +- } +- +- if (ovsthread_once_start(&offload_thread_once)) { +- xpthread_cond_init(&dp_flow_offload.cond, NULL); +- ovs_thread_create("dp_netdev_flow_offload", +- dp_netdev_flow_offload_main, NULL); +- ovsthread_once_done(&offload_thread_once); +- } +- +- if (flow->mark != INVALID_FLOW_MARK) { +- op = DP_NETDEV_FLOW_OFFLOAD_OP_MOD; +- } else { +- op = DP_NETDEV_FLOW_OFFLOAD_OP_ADD; +- } +- offload = dp_netdev_alloc_flow_offload(pmd, flow, op); +- offload->match = *match; +- offload->actions = xmalloc(actions_len); +- memcpy(offload->actions, actions, actions_len); +- offload->actions_len = actions_len; +- +- dp_netdev_append_flow_offload(offload); +-} +- + static void + dp_netdev_pmd_remove_flow(struct dp_netdev_pmd_thread *pmd, + struct dp_netdev_flow *flow) +@@ -2837,10 +3021,8 @@ dp_netdev_pmd_remove_flow(struct dp_netdev_pmd_thread *pmd, + ovs_assert(cls != NULL); + dpcls_remove(cls, &flow->cr); + cmap_remove(&pmd->flow_table, node, dp_netdev_flow_hash(&flow->ufid)); +- if (flow->mark != INVALID_FLOW_MARK) { +- queue_netdev_flow_del(pmd, flow); +- } + flow->dead = true; ++ queue_netdev_flow_del(pmd, flow); + + dp_netdev_flow_unref(flow); + } +@@ -2863,10 +3045,28 @@ dpif_netdev_flow_flush(struct dpif *dpif) + struct dp_netdev *dp = get_dp_netdev(dpif); + struct dp_netdev_pmd_thread *pmd; + ++#ifdef HAVE_HWOFF_AGENT ++ hwoff_func* funcs = hwoff_get_funcs(); ++ if (funcs->hwoff_set_offload_state != NULL) { ++ funcs->hwoff_set_offload_state(HWOFF_OFFLOAD_DISABLE); ++ } ++#endif ++ + CMAP_FOR_EACH (pmd, node, &dp->poll_threads) { + dp_netdev_pmd_flow_flush(pmd); ++#ifdef HAVE_HWOFF_AGENT ++ if (dp->dp_pmd_ukey_purge_cb) { ++ dp->dp_pmd_ukey_purge_cb(dp->dp_pmd_ukey_purge_aux, pmd->core_id); ++ } ++#endif + } + ++#ifdef HAVE_HWOFF_AGENT ++ if (funcs->hwoff_set_offload_state != NULL) { ++ funcs->hwoff_set_offload_state(HWOFF_OFFLOAD_ENABLE); ++ } ++#endif ++ + return 0; + } + +@@ -3480,6 +3680,9 @@ dp_netdev_flow_to_dpif_flow(const struct dp_netdev *dp, + } + + flow->ufid = netdev_flow->ufid; ++#ifdef HAVE_HWOFF_AGENT ++ flow->mega_ufid = netdev_flow->mega_ufid; ++#endif + flow->ufid_present = true; + flow->pmd_id = netdev_flow->pmd_id; + +@@ -3687,7 +3890,20 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd, + cmap_insert(&pmd->flow_table, CONST_CAST(struct cmap_node *, &flow->node), + dp_netdev_flow_hash(&flow->ufid)); + +- queue_netdev_flow_put(pmd, flow, match, actions, actions_len); ++#ifdef HAVE_HWOFF_AGENT ++ struct netdev *upcall_dev = NULL; ++ const char *dpif_type_str = dpif_normalize_type(pmd->dp->class->type); ++ ++ upcall_dev = netdev_ports_get(in_port, dpif_type_str); ++ hwoff_func* funcs = hwoff_get_funcs(); ++ if ((funcs->hwoff_is_support_offload == NULL) || ++ !funcs->hwoff_is_support_offload(upcall_dev)) { ++ queue_netdev_flow_put(pmd, flow, match, actions, actions_len, false); ++ } ++ netdev_close(upcall_dev); ++#else ++ queue_netdev_flow_put(pmd, flow, match, actions, actions_len, false); ++#endif + + if (OVS_UNLIKELY(!VLOG_DROP_DBG((&upcall_rl)))) { + struct ds ds = DS_EMPTY_INITIALIZER; +@@ -3738,6 +3954,70 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd, + return flow; + } + ++ ++#ifdef HAVE_HWOFF_AGENT ++static bool ++is_need_clear_forward_flow(struct dp_netdev_flow *netdev_flow, ++ struct eth_addr *temp_mac, uint32_t *dp_in_port) ++{ ++ struct hwoff_migrate_rarp_mac_entry *entry = NULL; ++ bool clear_flow = false; ++ struct hwoff_migrate_rarp_mac_infos *hwoff_migrate_rarp_mac_infos = hwoff_migrate_rarp_mac_infos_get(); ++ if (netdev_flow == NULL) { ++ return false; ++ } ++ if (hwoff_rarp_status_get()) { ++ ovs_rwlock_wrlock(&hwoff_migrate_rarp_mac_infos->rw); ++ if (unlikely(hwoff_migrate_rarp_mac_infos->length)) { ++ entry = hwoff_get_entry_by_mac(netdev_flow->flow.dl_src); ++ if (entry && entry->is_need_del) { ++ hwoff_rarp_mac_remove_from_list(entry); ++ (void)memcpy(&temp_mac->ea, &netdev_flow->flow.dl_src.ea, RTE_ETHER_ADDR_LEN); ++ *dp_in_port = netdev_flow->flow.in_port.odp_port; ++ clear_flow = true; ++ } ++ } ++ ovs_rwlock_unlock(&hwoff_migrate_rarp_mac_infos->rw); ++ } ++ return clear_flow; ++} ++ ++static void dp_netdev_clear_flow(struct dp_netdev_pmd_thread *pmd, struct eth_addr smac, uint32_t dp_in_port) ++{ ++ struct dp_netdev *dp = pmd->dp; ++ struct dp_netdev_flow *netdev_flow = NULL; ++ ovs_mutex_lock(&pmd->flow_mutex); ++ CMAP_FOR_EACH(netdev_flow, node, &pmd->flow_table) ++ { ++ if (hwoff_rarp_status_get() && ((eth_addr_equals(netdev_flow->flow.dl_src, smac) && ++ !eth_addr_is_broadcast(netdev_flow->flow.dl_dst) && dp_in_port != netdev_flow->flow.in_port.odp_port) || ++ (eth_addr_equals(netdev_flow->flow.dl_dst, smac) && !eth_addr_is_broadcast(netdev_flow->flow.dl_src)))) { ++ dp_netdev_pmd_remove_flow(pmd, netdev_flow); ++ } ++ } ++ ovs_mutex_unlock(&pmd->flow_mutex); ++ if (dp->dp_pmd_ukey_purge_cb) { ++ dp->dp_pmd_ukey_purge_cb(dp->dp_pmd_ukey_purge_aux, pmd->core_id); ++ } ++} ++ ++static void ++dp_netdev_clear_flow_by_rarp(struct dp_netdev_pmd_thread *pmd, struct dp_netdev_flow *flow) ++ OVS_ACQUIRES(pmd->flow_mutex) ++{ ++ uint32_t dp_in_port = 0; ++ struct eth_addr temp_mac; ++ (void)memset(&temp_mac, 0, sizeof(struct eth_addr)); ++ if (flow == NULL) { ++ return; ++ } ++ ++ if (hwoff_rarp_status_get() && is_need_clear_forward_flow(flow, &temp_mac, &dp_in_port)) { ++ dp_netdev_clear_flow(pmd, temp_mac, dp_in_port); ++ } ++} ++#endif ++ + static int + flow_put_on_pmd(struct dp_netdev_pmd_thread *pmd, + struct netdev_flow_key *key, +@@ -3748,7 +4028,6 @@ flow_put_on_pmd(struct dp_netdev_pmd_thread *pmd, + { + struct dp_netdev_flow *netdev_flow; + int error = 0; +- + if (stats) { + memset(stats, 0, sizeof *stats); + } +@@ -3774,7 +4053,7 @@ flow_put_on_pmd(struct dp_netdev_pmd_thread *pmd, + ovsrcu_set(&netdev_flow->actions, new_actions); + + queue_netdev_flow_put(pmd, netdev_flow, match, +- put->actions, put->actions_len); ++ put->actions, put->actions_len, true); + + if (stats) { + get_dpif_flow_status(pmd->dp, netdev_flow, stats, NULL); +@@ -3801,6 +4080,9 @@ flow_put_on_pmd(struct dp_netdev_pmd_thread *pmd, + } + } + ovs_mutex_unlock(&pmd->flow_mutex); ++#ifdef HAVE_HWOFF_AGENT ++ dp_netdev_clear_flow_by_rarp(pmd, netdev_flow); ++#endif + return error; + } + +@@ -3883,6 +4165,41 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put) + return error; + } + ++#ifdef HAVE_HWOFF_AGENT ++static void ++hwoff_pmd_del_reverse_flow(struct dp_netdev_flow *netdev_flow, struct dp_netdev_pmd_thread *pmd) ++{ ++ bool clear_flag = false; ++ struct dp_netdev *dp = pmd->dp; ++ struct eth_addr del_flow_smac = { 0 }; ++ struct dp_netdev_flow *netdev_flow_iter = NULL; ++ struct hwoff_migrate_rarp_mac_entry *entry = NULL; ++ struct hwoff_migrate_rarp_mac_infos *migrate_rarp_mac_infos = hwoff_migrate_rarp_mac_infos_get(); ++ ++ if (!hwoff_rarp_status_get()) { ++ return; ++ } ++ ++ if (unlikely(migrate_rarp_mac_infos->length) != 0) { ++ ovs_rwlock_wrlock(&migrate_rarp_mac_infos->rw); ++ entry = hwoff_get_entry_by_mac(netdev_flow->flow.dl_dst); ++ if (entry != NULL && entry->is_need_del == true) { ++ hwoff_rarp_mac_remove_from_list(entry); ++ CMAP_FOR_EACH(netdev_flow_iter, node, &pmd->flow_table) { ++ if (eth_addr_equals(netdev_flow_iter->flow.dl_src, netdev_flow->flow.dl_dst) && ++ !(eth_addr_is_broadcast(netdev_flow_iter->flow.dl_dst))) { ++ dp_netdev_pmd_remove_flow(pmd, netdev_flow_iter); ++ } ++ } ++ if (dp->dp_pmd_ukey_purge_cb) { ++ dp->dp_pmd_ukey_purge_cb(dp->dp_pmd_ukey_purge_aux, pmd->core_id); ++ } ++ } ++ ovs_rwlock_unlock(&migrate_rarp_mac_infos->rw); ++ } ++} ++#endif ++ + static int + flow_del_on_pmd(struct dp_netdev_pmd_thread *pmd, + struct dpif_flow_stats *stats, +@@ -3895,6 +4212,9 @@ flow_del_on_pmd(struct dp_netdev_pmd_thread *pmd, + netdev_flow = dp_netdev_pmd_find_flow(pmd, del->ufid, del->key, + del->key_len); + if (netdev_flow) { ++#ifdef HAVE_HWOFF_AGENT ++ hwoff_pmd_del_reverse_flow(netdev_flow, pmd); ++#endif + if (stats) { + get_dpif_flow_status(pmd->dp, netdev_flow, stats, NULL); + } +@@ -3903,7 +4223,6 @@ flow_del_on_pmd(struct dp_netdev_pmd_thread *pmd, + error = ENOENT; + } + ovs_mutex_unlock(&pmd->flow_mutex); +- + return error; + } + +@@ -4618,7 +4937,6 @@ dp_netdev_pmd_flush_output_on_port(struct dp_netdev_pmd_thread *pmd, + + output_cnt = dp_packet_batch_size(&p->output_pkts); + ovs_assert(output_cnt > 0); +- + netdev_send(p->port->netdev, tx_qid, &p->output_pkts, dynamic_txqs); + dp_packet_batch_init(&p->output_pkts); + +@@ -4665,6 +4983,76 @@ dp_netdev_pmd_flush_output_packets(struct dp_netdev_pmd_thread *pmd, + return output_cnt; + } + ++#ifdef HAVE_HWOFF_AGENT ++/* The struct is use to classift batch's packets into different batch according to ++ * packet's input port*/ ++#define HWOFF_NETDEV_NAME_LENGTH 100 ++struct hwoff_dpif_port_classifier_node { ++ odp_port_t port; ++ struct dp_packet_batch batch; ++}; ++ ++struct hwoff_dpif_port_classifier { ++ size_t node_count; ++ struct hwoff_dpif_port_classifier_node node[NETDEV_MAX_BURST]; ++}; ++ ++static odp_port_t ++hwoff_dpif_port_get_by_mbuf(struct dp_packet *packet) ++{ ++ uint16_t dpdk_port = 0; ++ char dev_name[HWOFF_NETDEV_NAME_LENGTH] = {0}; ++ odp_port_t port_no = ODPP_NONE; ++ ++ if (packet != NULL) { ++ dpdk_port = packet->mbuf.port; ++ hwoff_netdev_name_get(dpdk_port, dev_name, HWOFF_NETDEV_NAME_LENGTH); ++ port_no = dpif_netdev_get_odp_no_by_name(dev_name); ++ } ++ return port_no; ++} ++ ++static void ++hwoff_dpif_port_classift_insert(odp_port_t port_no, struct dp_packet *packet, ++ struct hwoff_dpif_port_classifier *port_classifile) ++{ ++ struct hwoff_dpif_port_classifier_node *node = NULL; ++ for (int i = 0; i < port_classifile->node_count; ++i) { ++ if (port_classifile->node[i].port == port_no) { ++ node = &port_classifile->node[i]; ++ node->batch.packets[node->batch.count++] = packet; ++ return; ++ } ++ } ++ ++ node = &port_classifile->node[port_classifile->node_count++]; ++ node->port = port_no; ++ node->batch.packets[node->batch.count++] = packet; ++ return; ++} ++ ++/* redistribute batch's packets, current scheme is to iterate through an packets array ++ of max size 32*/ ++static void ++hwoff_dpif_mbuf_packets_classify(const struct dp_packet_batch *packets, ++ struct hwoff_dpif_port_classifier *port_classifile) ++{ ++ if (packets == NULL || port_classifile == NULL) { ++ return; ++ } ++ ++ odp_port_t port_no = ODPP_NONE; ++ ++ for (int i = 0; i < packets->count; ++i) { ++ port_no = hwoff_dpif_port_get_by_mbuf(packets->packets[i]); ++ if (port_no != ODPP_NONE) { ++ hwoff_dpif_port_classift_insert(port_no, packets->packets[i], port_classifile); ++ } ++ } ++ return; ++} ++#endif ++ + static int + dp_netdev_process_rxq_port(struct dp_netdev_pmd_thread *pmd, + struct dp_netdev_rxq *rxq, +@@ -4707,9 +5095,24 @@ dp_netdev_process_rxq_port(struct dp_netdev_pmd_thread *pmd, + } + } + } ++ ++#ifdef HAVE_HWOFF_AGENT ++ struct hwoff_dpif_port_classifier port_classifier = {0}; ++ /* When the rxq is be shared, redistribute packets in batch ++ * according to packet's input port */ ++ if (hwoff_netdev_is_shared(rxq->rx->netdev)) { ++ hwoff_dpif_mbuf_packets_classify(&batch, &port_classifier); ++ for (int i = 0; i < port_classifier.node_count; ++i) { ++ dp_netdev_input(pmd, &port_classifier.node[i].batch, ++ port_classifier.node[i].port); ++ } ++ } else { ++ dp_netdev_input(pmd, &batch, port_no); ++ } ++#else + /* Process packet batch. */ + dp_netdev_input(pmd, &batch, port_no); +- ++#endif + /* Assign processing cycles to rx queue. */ + cycles = cycle_timer_stop(&pmd->perf_stats, &timer); + dp_netdev_rxq_add_cycles(rxq, RXQ_CYCLES_PROC_CURR, cycles); +@@ -4991,11 +5394,23 @@ rxq_scheduling(struct dp_netdev *dp, bool pinned) OVS_REQUIRES(dp->port_mutex) + int numa_id; + bool assign_cyc = dp->pmd_rxq_assign_cyc; + ++#ifdef HAVE_HWOFF_AGENT ++ /* share upcall queues just will be assigned once to avoid invalid poll*/ ++ bool share_upcall_scheduled = false; ++#endif ++ + HMAP_FOR_EACH (port, node, &dp->ports) { + if (!netdev_is_pmd(port->netdev)) { + continue; + } + ++#ifdef HAVE_HWOFF_AGENT ++ bool is_share_upcall_dev = hwoff_netdev_is_shared(port->netdev); ++ if (share_upcall_scheduled && is_share_upcall_dev) { ++ continue; ++ } ++#endif ++ + for (int qid = 0; qid < port->n_rxq; qid++) { + struct dp_netdev_rxq *q = &port->rxqs[qid]; + +@@ -5037,6 +5452,11 @@ rxq_scheduling(struct dp_netdev *dp, bool pinned) OVS_REQUIRES(dp->port_mutex) + rxqs[n_rxqs++] = q; + } + } ++#ifdef HAVE_HWOFF_AGENT ++ if (is_share_upcall_dev) { ++ share_upcall_scheduled = true; ++ } ++#endif + } + + if (n_rxqs > 1 && assign_cyc) { +@@ -5102,7 +5522,6 @@ reload_affected_pmds(struct dp_netdev *dp) + + CMAP_FOR_EACH (pmd, node, &dp->poll_threads) { + if (pmd->need_reload) { +- flow_mark_flush(pmd); + dp_netdev_reload_pmd__(pmd); + } + } +@@ -6508,7 +6927,19 @@ dp_netdev_destroy_pmd(struct dp_netdev_pmd_thread *pmd) + { + struct dpcls *cls; + ++#ifdef HAVE_HWOFF_AGENT ++ hwoff_func* funcs = hwoff_get_funcs(); ++ if (funcs->hwoff_set_offload_state != NULL) { ++ funcs->hwoff_set_offload_state(HWOFF_OFFLOAD_DISABLE); ++ } ++#endif + dp_netdev_pmd_flow_flush(pmd); ++#ifdef HAVE_HWOFF_AGENT ++ if (funcs->hwoff_set_offload_state != NULL) { ++ funcs->hwoff_set_offload_state(HWOFF_OFFLOAD_ENABLE); ++ } ++#endif ++ + hmap_destroy(&pmd->send_port_cache); + hmap_destroy(&pmd->tnl_port_cache); + hmap_destroy(&pmd->tx_ports); +@@ -6842,6 +7273,7 @@ dpif_netdev_packet_get_rss_hash(struct dp_packet *packet, + return hash; + } + ++#ifndef HAVE_HWOFF_AGENT + struct packet_batch_per_flow { + unsigned int byte_count; + uint16_t tcp_flags; +@@ -6849,6 +7281,7 @@ struct packet_batch_per_flow { + + struct dp_packet_batch array; + }; ++#endif + + static inline void + packet_batch_per_flow_update(struct packet_batch_per_flow *batch, +@@ -6885,6 +7318,10 @@ packet_batch_per_flow_execute(struct packet_batch_per_flow *batch, + + actions = dp_netdev_flow_get_actions(flow); + ++#ifdef HAVE_HWOFF_AGENT ++ dp_netdev_flow_offload_add(pmd, flow, NULL, &batch->array, actions->actions, actions->size); ++#endif ++ + dp_netdev_execute_actions(pmd, &batch->array, true, &flow->flow, + actions->actions, actions->size); + } +@@ -7203,6 +7640,9 @@ handle_packet_upcall(struct dp_netdev_pmd_thread *pmd, + uint32_t hash = dp_netdev_flow_hash(&netdev_flow->ufid); + smc_insert(pmd, key, hash); + emc_probabilistic_insert(pmd, key, netdev_flow); ++#ifdef HAVE_HWOFF_AGENT ++ dp_netdev_clear_flow_by_rarp(pmd, netdev_flow); ++#endif + } + if (pmd_perf_metrics_enabled(pmd)) { + /* Update upcall stats. */ +@@ -7273,6 +7713,9 @@ fast_path_processing(struct dp_netdev_pmd_thread *pmd, + if (netdev_flow) { + lookup_cnt += add_lookup_cnt; + rules[i] = &netdev_flow->cr; ++#ifdef HAVE_HWOFF_AGENT ++ dp_netdev_clear_flow_by_rarp(pmd, netdev_flow); ++#endif + continue; + } + +@@ -7382,12 +7825,12 @@ dp_netdev_input__(struct dp_netdev_pmd_thread *pmd, + + /* All the flow batches need to be reset before any call to + * packet_batch_per_flow_execute() as it could potentially trigger +- * recirculation. When a packet matching flow ‘j’ happens to be ++ * recirculation. When a packet matching flow 'j' happens to be + * recirculated, the nested call to dp_netdev_input__() could potentially + * classify the packet as matching another flow - say 'k'. It could happen + * that in the previous call to dp_netdev_input__() that same flow 'k' had + * already its own batches[k] still waiting to be served. So if its +- * ‘batch’ member is not reset, the recirculated packet would be wrongly ++ * 'batch' member is not reset, the recirculated packet would be wrongly + * appended to batches[k] of the 1st call to dp_netdev_input__(). */ + for (i = 0; i < n_batches; i++) { + batches[i].flow->batch = NULL; +@@ -7435,6 +7878,16 @@ dpif_netdev_register_upcall_cb(struct dpif *dpif, upcall_callback *cb, + dp->upcall_aux = aux; + dp->upcall_cb = cb; + } ++#ifdef HAVE_HWOFF_AGENT ++static void ++dpif_netdev_register_pmd_ukey_purge_cb(struct dpif *dpif, dp_pmd_ukey_purge_callback *cb, ++ void *aux) ++{ ++ struct dp_netdev *dp = get_dp_netdev(dpif); ++ dp->dp_pmd_ukey_purge_cb = cb; ++ dp->dp_pmd_ukey_purge_aux = aux; ++} ++#endif + + static void + dpif_netdev_xps_revalidate_pmd(const struct dp_netdev_pmd_thread *pmd, +@@ -7517,6 +7970,21 @@ pmd_send_port_cache_lookup(const struct dp_netdev_pmd_thread *pmd, + return tx_port_lookup(&pmd->send_port_cache, port_no); + } + ++#ifdef HAVE_HWOFF_AGENT ++struct netdev * ++dp_get_outdev_from_pmd(odp_port_t port_no, void *tmp_pmd) ++{ ++ struct dp_netdev_pmd_thread *pmd = (struct dp_netdev_pmd_thread *)tmp_pmd; ++ struct tx_port *p = pmd_send_port_cache_lookup(pmd, port_no); ++ ++ if (p != NULL && p->port != NULL) { ++ return p->port->netdev; ++ } ++ ++ return NULL; ++} ++#endif ++ + static int + push_tnl_action(const struct dp_netdev_pmd_thread *pmd, + const struct nlattr *attr, +@@ -8046,6 +8514,18 @@ dpif_netdev_ct_dump_done(struct dpif *dpif OVS_UNUSED, + return err; + } + ++#ifdef HAVE_HWOFF_AGENT ++static int ++dpif_netdev_ct_flush(struct dpif *dpif, const uint16_t *zone, ++ union ct_addr *sip, union ct_addr *dip, ++ union ct_addr *smask, union ct_addr *dmask, ++ uint16_t dl_type, bool is_force) ++{ ++ struct dp_netdev *dp = get_dp_netdev(dpif); ++ ++ return conntrack_flush(dp->conntrack, zone, sip, dip, smask, dmask, dl_type, is_force); ++} ++#else + static int + dpif_netdev_ct_flush(struct dpif *dpif, const uint16_t *zone, + const struct ct_dpif_tuple *tuple) +@@ -8057,6 +8537,7 @@ dpif_netdev_ct_flush(struct dpif *dpif, const uint16_t *zone, + } + return conntrack_flush(dp->conntrack, zone); + } ++#endif + + static int + dpif_netdev_ct_set_maxconns(struct dpif *dpif, uint32_t maxconns) +@@ -8459,6 +8940,9 @@ const struct dpif_class dpif_netdev_class = { + dpif_netdev_bond_add, + dpif_netdev_bond_del, + dpif_netdev_bond_stats_get, ++#ifdef HAVE_HWOFF_AGENT ++ dpif_netdev_register_pmd_ukey_purge_cb, ++#endif + }; + + static void +diff --git a/openvswitch-2.14.2/lib/dpif-netdev.h b/openvswitch-2.14.2/lib/dpif-netdev.h +index 6db6ed2..84e8b6c 100644 +--- a/openvswitch-2.14.2/lib/dpif-netdev.h ++++ b/openvswitch-2.14.2/lib/dpif-netdev.h +@@ -24,6 +24,9 @@ + #include "openvswitch/types.h" + #include "dp-packet.h" + #include "packets.h" ++#ifdef HAVE_HWOFF_AGENT ++#include "ovs-atomic.h" ++#endif + + #ifdef __cplusplus + extern "C" { +@@ -34,6 +37,12 @@ extern "C" { + enum { DP_NETDEV_HEADROOM = 2 + VLAN_HEADER_LEN }; + + bool dpif_is_netdev(const struct dpif *); ++odp_port_t dpif_netdev_get_odp_no_by_name(const char *devname); ++#ifdef HAVE_HWOFF_AGENT ++struct netdev * dp_get_outdev_from_pmd(odp_port_t port_no, void *tmp_pmd); ++bool dp_netdev_flow_dead_status_get(void *flow); ++void dp_netdev_hwoff_switch_set(int value); ++#endif + + #define NR_QUEUE 1 + #define NR_PMD_THREADS 1 +diff --git a/openvswitch-2.14.2/lib/dpif-netlink.c b/openvswitch-2.14.2/lib/dpif-netlink.c +index 2f881e4..6ad0f59 100644 +--- a/openvswitch-2.14.2/lib/dpif-netlink.c ++++ b/openvswitch-2.14.2/lib/dpif-netlink.c +@@ -2098,6 +2098,7 @@ parse_flow_put(struct dpif_netlink *dpif, struct dpif_flow_put *put) + + info.tp_dst_port = dst_port; + info.tunnel_csum_on = csum_on; ++ info.pmd = NULL; + info.recirc_id_shared_with_tc = (dpif->user_features + & OVS_DP_F_TC_RECIRC_SHARING); + info.tc_modify_flow_deleted = false; +@@ -2900,6 +2901,20 @@ dpif_netlink_ct_dump_done(struct dpif *dpif OVS_UNUSED, + return err; + } + ++#ifdef HAVE_HWOFF_AGENT ++static int ++dpif_netlink_ct_flush(struct dpif *dpif OVS_UNUSED, const uint16_t *zone, ++ union ct_addr *sip OVS_UNUSED, union ct_addr *dip OVS_UNUSED, ++ union ct_addr *smask OVS_UNUSED, union ct_addr *dmask OVS_UNUSED, ++ uint16_t dl_type OVS_UNUSED, bool is_force OVS_UNUSED) ++{ ++ if (zone) { ++ return nl_ct_flush_zone(*zone); ++ } else { ++ return nl_ct_flush(); ++ } ++} ++#else + static int + dpif_netlink_ct_flush(struct dpif *dpif OVS_UNUSED, const uint16_t *zone, + const struct ct_dpif_tuple *tuple) +@@ -2912,6 +2927,7 @@ dpif_netlink_ct_flush(struct dpif *dpif OVS_UNUSED, const uint16_t *zone, + return nl_ct_flush(); + } + } ++#endif + + static int + dpif_netlink_ct_set_limits(struct dpif *dpif OVS_UNUSED, +@@ -4012,6 +4028,9 @@ const struct dpif_class dpif_netlink_class = { + NULL, /* bond_add */ + NULL, /* bond_del */ + NULL, /* bond_stats_get */ ++#ifdef HAVE_HWOFF_AGENT ++ NULL, ++#endif + }; + + static int +diff --git a/openvswitch-2.14.2/lib/dpif-provider.h b/openvswitch-2.14.2/lib/dpif-provider.h +index 0e024c1..c7463a7 100644 +--- a/openvswitch-2.14.2/lib/dpif-provider.h ++++ b/openvswitch-2.14.2/lib/dpif-provider.h +@@ -25,6 +25,7 @@ + #include "openflow/openflow.h" + #include "dpif.h" + #include "util.h" ++#include "conntrack.h" + + #ifdef __cplusplus + extern "C" { +@@ -463,8 +464,14 @@ struct dpif_class { + * - If 'tuple' is not NULL, flush the conntrack entry specified by + * 'tuple' in '*zone'. If 'zone' is NULL, use the default zone + * (zone 0). */ ++#ifdef HAVE_HWOFF_AGENT ++ int (*ct_flush)(struct dpif *, const uint16_t *zone, ++ union ct_addr *sip, union ct_addr *dip, ++ union ct_addr *smask, union ct_addr *dmask, uint16_t dl_type, bool is_force); ++#else + int (*ct_flush)(struct dpif *, const uint16_t *zone, + const struct ct_dpif_tuple *tuple); ++#endif + /* Set max connections allowed. */ + int (*ct_set_maxconns)(struct dpif *, uint32_t maxconns); + /* Get max connections allowed. */ +@@ -628,6 +635,9 @@ struct dpif_class { + * sufficient to store BOND_BUCKETS number of elements. */ + int (*bond_stats_get)(struct dpif *dpif, uint32_t bond_id, + uint64_t *n_bytes); ++#ifdef HAVE_HWOFF_AGENT ++ void (*register_dp_pmd_ukey_purge_cb)(struct dpif *, dp_pmd_ukey_purge_callback *, void *aux); ++#endif + }; + + extern const struct dpif_class dpif_netlink_class; +diff --git a/openvswitch-2.14.2/lib/dpif.c b/openvswitch-2.14.2/lib/dpif.c +index 7cac3a6..0579dd2 100644 +--- a/openvswitch-2.14.2/lib/dpif.c ++++ b/openvswitch-2.14.2/lib/dpif.c +@@ -597,7 +597,7 @@ dpif_port_add(struct dpif *dpif, struct netdev *netdev, odp_port_t *port_nop) + VLOG_DBG_RL(&dpmsg_rl, "%s: added %s as port %"PRIu32, + dpif_name(dpif), netdev_name, port_no); + +- if (!dpif_is_tap_port(netdev_get_type(netdev))) { ++ // if (!dpif_is_tap_port(netdev_get_type(netdev))) { + + const char *dpif_type_str = dpif_normalize_type(dpif_type(dpif)); + struct dpif_port dpif_port; +@@ -606,7 +606,7 @@ dpif_port_add(struct dpif *dpif, struct netdev *netdev, odp_port_t *port_nop) + dpif_port.name = CONST_CAST(char *, netdev_name); + dpif_port.port_no = port_no; + netdev_ports_insert(netdev, dpif_type_str, &dpif_port); +- } ++ // } + } else { + VLOG_WARN_RL(&error_rl, "%s: failed to add %s as port: %s", + dpif_name(dpif), netdev_name, ovs_strerror(error)); +@@ -1504,6 +1504,15 @@ dpif_register_upcall_cb(struct dpif *dpif, upcall_callback *cb, void *aux) + } + } + ++#ifdef HAVE_HWOFF_AGENT ++void ++dpif_register_dp_pmd_ukey_purge_cb(struct dpif *dpif, dp_pmd_ukey_purge_callback *cb, void *aux) ++{ ++ if (dpif->dpif_class->register_dp_pmd_ukey_purge_cb) { ++ dpif->dpif_class->register_dp_pmd_ukey_purge_cb(dpif, cb, aux); ++ } ++} ++#endif + void + dpif_enable_upcall(struct dpif *dpif) + { +diff --git a/openvswitch-2.14.2/lib/dpif.h b/openvswitch-2.14.2/lib/dpif.h +index 2d52f01..9b7a1d0 100644 +--- a/openvswitch-2.14.2/lib/dpif.h ++++ b/openvswitch-2.14.2/lib/dpif.h +@@ -598,6 +598,9 @@ struct dpif_flow { + const struct nlattr *actions; /* Actions, as OVS_ACTION_ATTR_ */ + size_t actions_len; /* 'actions' length in bytes. */ + ovs_u128 ufid; /* Unique flow identifier. */ ++#ifdef HAVE_HWOFF_AGENT ++ ovs_u128 mega_ufid; /* Flow mega identifier. */ ++#endif + bool ufid_present; /* True if 'ufid' was provided by datapath.*/ + unsigned pmd_id; /* Datapath poll mode driver id. */ + struct dpif_flow_stats stats; /* Flow statistics. */ +@@ -838,6 +841,10 @@ struct dpif_upcall { + + void dpif_register_dp_purge_cb(struct dpif *, dp_purge_callback *, void *aux); + ++#ifdef HAVE_HWOFF_AGENT ++typedef void dp_pmd_ukey_purge_callback(void *auc, unsigned pmd_id); ++void dpif_register_dp_pmd_ukey_purge_cb(struct dpif * dpif, dp_pmd_ukey_purge_callback *, void * aux); ++#endif + /* A callback to process an upcall, currently implemented only by dpif-netdev. + * + * The caller provides the 'packet' and 'flow' to process, the corresponding +diff --git a/openvswitch-2.14.2/lib/hwoff_init_func.c b/openvswitch-2.14.2/lib/hwoff_init_func.c +new file mode 100644 +index 0000000..6812b57 +--- /dev/null ++++ b/openvswitch-2.14.2/lib/hwoff_init_func.c +@@ -0,0 +1,74 @@ ++/* ++ * Copyright (c) 2021 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. ++ */ ++#include "hwoff_init_func.h" ++#include ++ ++#define HWOFF_SHARED_LIB "libdpak_ovs.so" ++#define ADD_FUNC(name) {#name, (void**)&hwoff_funcs.name} ++ ++typedef struct { ++ const char *name; ++ void **func; ++} func_cfg; ++ ++static hwoff_func hwoff_funcs = {0}; ++ ++static func_cfg func_cfgs[] = { ++ ADD_FUNC(hwoff_rte_flow_query_count), ++ ADD_FUNC(hwoff_rte_flow_create), ++ ADD_FUNC(hwoff_rte_flow_destroy), ++ ADD_FUNC(hwoff_is_hiovs_netdev), ++ ADD_FUNC(hwoff_get_eth_vport_id), ++ ADD_FUNC(hwoff_is_support_offload), ++ ADD_FUNC(hwoff_rte_flow_alloc), ++ ADD_FUNC(hwoff_rte_flow_dealloc), ++ ADD_FUNC(hwoff_rte_flow_deleted_set), ++ ADD_FUNC(hwoff_rte_flow_deleted_get), ++ ADD_FUNC(hwoff_global_add_vxlan_vtep), ++ ADD_FUNC(hwoff_global_del_vxlan_vtep), ++ ADD_FUNC(hwoff_tnl_get_src_port), ++ ADD_FUNC(hwoff_parse_ovs_other_config), ++ ADD_FUNC(hwoff_is_ethdev), ++ ADD_FUNC(hwoff_set_offload_state), ++ ADD_FUNC(hwoff_set_qos), ++ ADD_FUNC(hwoff_set_ingress_policing), ++ ADD_FUNC(hwoff_parse_vf_extra_options), ++}; ++ ++hwoff_func* hwoff_get_funcs(void) ++{ ++ return &hwoff_funcs; ++} ++ ++int hwoff_funcs_init(void) ++{ ++ void *handler = dlopen(HWOFF_SHARED_LIB, RTLD_NOW); ++ if (handler == NULL) { ++ RTE_LOG(ERR, EAL, "%s load err %s \n", HWOFF_SHARED_LIB, dlerror()); ++ return -1; ++ } ++ ++ for (int index = 0; index < ARRAY_SIZE(func_cfgs); index++) { ++ *func_cfgs[index].func = dlsym(handler, func_cfgs[index].name); ++ if (*func_cfgs[index].func == NULL) { ++ RTE_LOG(ERR, EAL, "%s load func %s fail: %s", HWOFF_SHARED_LIB, func_cfgs[index].name, dlerror()); ++ dlclose(handler); ++ return -1; ++ } ++ } ++ ++ return 0; ++} +diff --git a/openvswitch-2.14.2/lib/hwoff_init_func.h b/openvswitch-2.14.2/lib/hwoff_init_func.h +new file mode 100644 +index 0000000..a13b012 +--- /dev/null ++++ b/openvswitch-2.14.2/lib/hwoff_init_func.h +@@ -0,0 +1,50 @@ ++/* ++ * Copyright (c) 2021 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 HWOFF_INIT_FUNC_H ++#define HWOFF_INIT_FUNC_H 1 ++ ++#include "dpak_ovs.h" ++ ++typedef struct { ++ int (*hwoff_rte_flow_query_count)(struct netdev *netdev, struct rte_flow *rte_flow, ++ struct rte_flow_query_count *query, struct rte_flow_error *error); ++ struct rte_flow *(*hwoff_rte_flow_create)(struct netdev *netdev, const struct rte_flow_attr *attr, ++ const struct rte_flow_item *items, const struct rte_flow_action *actions, ++ struct rte_flow_error *error); ++ int (*hwoff_rte_flow_destroy)(struct netdev *netdev, struct rte_flow *rte_flow, ++ struct rte_flow_error *error); ++ uint32_t (*hwoff_get_eth_vport_id)(struct netdev *netdev); ++ bool (*hwoff_is_hiovs_netdev)(const struct netdev *netdev); ++ bool (*hwoff_is_ethdev)(const struct netdev *netdev); ++ bool (*hwoff_is_support_offload)(const struct netdev *netdev); ++ struct rte_flow* (*hwoff_rte_flow_alloc)(ovs_u128 *ufid, void* flow_data); ++ void (*hwoff_rte_flow_dealloc)(struct rte_flow *flow); ++ void (*hwoff_rte_flow_deleted_set)(struct rte_flow *flow, bool flag); ++ bool (*hwoff_rte_flow_deleted_get)(struct rte_flow *flow); ++ int (*hwoff_global_add_vxlan_vtep)(bool is_ipv6, uint8_t *vxlan_dstip, uint32_t length, uint16_t dst_port); ++ int (*hwoff_global_del_vxlan_vtep)(bool is_ipv6, uint8_t *vxlan_dstip, uint32_t length); ++ uint16_t (*hwoff_tnl_get_src_port)(struct dp_packet *one_pkt); ++ void (*hwoff_parse_ovs_other_config)(const struct smap *ovs_other_config); ++ void (*hwoff_set_offload_state)(hwoff_offload_state_t offload); ++ void (*hwoff_set_qos)(uint16_t port_id, const char *type, const struct smap *details); ++ void (*hwoff_set_ingress_policing)(uint16_t port_id, uint32_t policer_rate, uint32_t policer_burst); ++ int (*hwoff_parse_vf_extra_options)(uint16_t dpdk_port_id, const struct smap *port_config); ++} hwoff_func; ++ ++hwoff_func* hwoff_get_funcs(void); ++int hwoff_funcs_init(void); ++#endif +diff --git a/openvswitch-2.14.2/lib/mac-learning.c b/openvswitch-2.14.2/lib/mac-learning.c +index f618348..d0644b3 100644 +--- a/openvswitch-2.14.2/lib/mac-learning.c ++++ b/openvswitch-2.14.2/lib/mac-learning.c +@@ -29,12 +29,95 @@ + #include "unaligned.h" + #include "util.h" + #include "vlan-bitmap.h" +- ++#include "openvswitch/vlog.h" ++VLOG_DEFINE_THIS_MODULE(mac_learning); + COVERAGE_DEFINE(mac_learning_learned); + COVERAGE_DEFINE(mac_learning_expired); + COVERAGE_DEFINE(mac_learning_evicted); + COVERAGE_DEFINE(mac_learning_moved); + ++ ++#ifdef HAVE_HWOFF_AGENT ++static struct hwoff_migrate_rarp_mac_infos g_hwoff_migrate_rarp_mac_infos; ++static bool g_hwoff_rarp_enabled = false; ++ ++struct hwoff_migrate_rarp_mac_infos *hwoff_migrate_rarp_mac_infos_get(void) ++{ ++ return &g_hwoff_migrate_rarp_mac_infos; ++} ++ ++bool hwoff_rarp_status_get(void) ++{ ++ return g_hwoff_rarp_enabled; ++} ++ ++struct hwoff_migrate_rarp_mac_entry *hwoff_get_entry_by_mac(struct eth_addr mac) ++{ ++ struct hwoff_migrate_rarp_mac_entry *entry_iter = NULL; ++ ++ HMAP_FOR_EACH_WITH_HASH (entry_iter, mac_entry_node, hash_mac(mac, 0, 0), ++ &g_hwoff_migrate_rarp_mac_infos.mac_info_list) { ++ if (eth_addr_equals(entry_iter->mac_addr, mac)) { ++ return entry_iter; ++ } ++ } ++ ++ return NULL; ++} ++ ++struct hwoff_migrate_rarp_mac_entry * hwoff_rarp_mac_insert_to_list(struct eth_addr mac) ++{ ++ if (g_hwoff_migrate_rarp_mac_infos.length >= HWOFF_MIGRATE_MAC_MAX_NUM) { ++ return NULL; ++ } ++ ++ uint32_t hash = hash_mac(mac, 0, 0); ++ struct hwoff_migrate_rarp_mac_entry *entry = hwoff_get_entry_by_mac(mac); ++ if (entry == NULL) { ++ entry = xmalloc(sizeof *entry); ++ memcpy(&entry->mac_addr.ea, &mac.ea, ETH_ADDR_LEN); ++ hmap_insert(&g_hwoff_migrate_rarp_mac_infos.mac_info_list, &entry->mac_entry_node, hash); ++ g_hwoff_migrate_rarp_mac_infos.length++; ++ } ++ ++ entry->is_need_del = true; ++ return entry; ++} ++ ++void hwoff_rarp_mac_remove_from_list(struct hwoff_migrate_rarp_mac_entry *entry) ++{ ++ if (OVS_UNLIKELY(entry == NULL)) { ++ return; ++ } ++ hmap_remove(&g_hwoff_migrate_rarp_mac_infos.mac_info_list, &entry->mac_entry_node); ++ g_hwoff_migrate_rarp_mac_infos.length--; ++ free(entry); ++ entry = NULL; ++} ++ ++void hwoff_rarp_mac_list_init(void) ++{ ++ hmap_init(&g_hwoff_migrate_rarp_mac_infos.mac_info_list); ++ ovs_rwlock_init(&g_hwoff_migrate_rarp_mac_infos.rw); ++ g_hwoff_migrate_rarp_mac_infos.length = 0; ++ g_hwoff_rarp_enabled = true; ++} ++ ++void hwoff_rarp_mac_list_uninit(void) ++{ ++ struct hwoff_migrate_rarp_mac_entry *entry_iter = NULL, *next_entry_iter = NULL; ++ ++ ovs_rwlock_wrlock(&g_hwoff_migrate_rarp_mac_infos.rw); ++ HMAP_FOR_EACH_SAFE (entry_iter, next_entry_iter, mac_entry_node, &g_hwoff_migrate_rarp_mac_infos.mac_info_list) { ++ hwoff_rarp_mac_remove_from_list(entry_iter); ++ } ++ hmap_destroy(&g_hwoff_migrate_rarp_mac_infos.mac_info_list); ++ g_hwoff_migrate_rarp_mac_infos.length = 0; ++ ovs_rwlock_unlock(&g_hwoff_migrate_rarp_mac_infos.rw); ++ g_hwoff_rarp_enabled = false; ++} ++#endif ++ + /* Returns the number of seconds since 'e' (within 'ml') was last learned. */ + int + mac_entry_age(const struct mac_learning *ml, const struct mac_entry *e) +@@ -410,12 +493,21 @@ is_mac_learning_update_needed(const struct mac_learning *ml, + * Keep the code here synchronized with that in is_mac_learning_update_needed() + * above. */ + static bool ++#ifdef HAVE_HWOFF_AGENT ++update_learning_table__(struct mac_learning *ml, struct eth_addr src, ++ int vlan, bool is_gratuitous_arp, bool is_bond, ++ void *in_port, void **out_port) ++#else + update_learning_table__(struct mac_learning *ml, struct eth_addr src, + int vlan, bool is_gratuitous_arp, bool is_bond, + void *in_port) ++#endif + OVS_REQ_WRLOCK(ml->rwlock) + { + struct mac_entry *mac; ++#ifdef HAVE_HWOFF_AGENT ++ void *mac_port = NULL; ++#endif + + if (!mac_learning_may_learn(ml, src, vlan)) { + return false; +@@ -437,7 +529,19 @@ update_learning_table__(struct mac_learning *ml, struct eth_addr src, + return false; + } + } +- ++#ifdef HAVE_HWOFF_AGENT ++ mac_port = mac_entry_get_port(ml, mac); ++ if (mac_port != in_port) { ++ if (mac_port) { ++ COVERAGE_INC(mac_learning_moved); ++ if (out_port) { ++ *out_port = mac_port; ++ } ++ } ++ mac_entry_set_port(ml, mac, in_port); ++ return true; ++ } ++#else + if (mac_entry_get_port(ml, mac) != in_port) { + if (mac_entry_get_port(ml, mac) != NULL) { + COVERAGE_INC(mac_learning_moved); +@@ -446,6 +550,7 @@ update_learning_table__(struct mac_learning *ml, struct eth_addr src, + mac_entry_set_port(ml, mac, in_port); + return true; + } ++#endif + return false; + } + +@@ -455,11 +560,19 @@ update_learning_table__(struct mac_learning *ml, struct eth_addr src, + * 'is_bond' is 'true'. + * + * Returns 'true' if 'ml' was updated, 'false' otherwise. */ ++#ifdef HAVE_HWOFF_AGENT ++bool ++mac_learning_update(struct mac_learning *ml, struct eth_addr src, ++ int vlan, bool is_gratuitous_arp, bool is_bond, ++ void *in_port, void **out_port) ++ OVS_EXCLUDED(ml->rwlock) ++#else + bool + mac_learning_update(struct mac_learning *ml, struct eth_addr src, + int vlan, bool is_gratuitous_arp, bool is_bond, + void *in_port) + OVS_EXCLUDED(ml->rwlock) ++#endif + { + bool need_update; + bool updated = false; +@@ -476,8 +589,13 @@ mac_learning_update(struct mac_learning *ml, struct eth_addr src, + if (need_update) { + /* Slow path: MAC learning table might need an update. */ + ovs_rwlock_wrlock(&ml->rwlock); ++#ifdef HAVE_HWOFF_AGENT ++ updated = update_learning_table__(ml, src, vlan, is_gratuitous_arp, ++ is_bond, in_port, out_port); ++#else + updated = update_learning_table__(ml, src, vlan, is_gratuitous_arp, + is_bond, in_port); ++#endif + ovs_rwlock_unlock(&ml->rwlock); + } + } +diff --git a/openvswitch-2.14.2/lib/mac-learning.h b/openvswitch-2.14.2/lib/mac-learning.h +index ad2f1fe..dd53a0e 100644 +--- a/openvswitch-2.14.2/lib/mac-learning.h ++++ b/openvswitch-2.14.2/lib/mac-learning.h +@@ -118,6 +118,30 @@ struct mac_entry { + struct ovs_list port_lru_node; /* In mac_learning_port's "port_lru"s. */ + }; + ++#ifdef HAVE_HWOFF_AGENT ++#define HWOFF_MIGRATE_MAC_MAX_NUM 8192 ++ ++struct hwoff_migrate_rarp_mac_entry { ++ bool is_need_del; ++ struct eth_addr mac_addr; ++ struct hmap_node mac_entry_node; ++}; ++ ++struct hwoff_migrate_rarp_mac_infos { ++ struct ovs_rwlock rw; ++ struct hmap mac_info_list; ++ uint64_t length; ++}; ++ ++void hwoff_rarp_mac_list_init(void); ++bool hwoff_rarp_status_get(void); ++void hwoff_rarp_mac_list_uninit(void); ++struct hwoff_migrate_rarp_mac_infos *hwoff_migrate_rarp_mac_infos_get(void); ++void hwoff_rarp_mac_remove_from_list(struct hwoff_migrate_rarp_mac_entry *e); ++struct hwoff_migrate_rarp_mac_entry *hwoff_get_entry_by_mac(struct eth_addr mac); ++struct hwoff_migrate_rarp_mac_entry * hwoff_rarp_mac_insert_to_list(struct eth_addr mac); ++ ++#endif + static inline void *mac_entry_get_port(const struct mac_learning *ml, + const struct mac_entry *); + void mac_entry_set_port(struct mac_learning *, struct mac_entry *, void *port); +@@ -214,10 +238,19 @@ struct mac_entry *mac_learning_insert(struct mac_learning *ml, + const struct eth_addr src, + uint16_t vlan) + OVS_REQ_WRLOCK(ml->rwlock); +-bool mac_learning_update(struct mac_learning *ml, struct eth_addr src, +- int vlan, bool is_gratuitous_arp, bool is_bond, +- void *in_port) ++#ifdef HAVE_HWOFF_AGENT ++bool ++mac_learning_update(struct mac_learning *ml, struct eth_addr src, ++ int vlan, bool is_gratuitous_arp, bool is_bond, ++ void *in_port, void **out_port) ++ OVS_EXCLUDED(ml->rwlock); ++#else ++bool ++mac_learning_update(struct mac_learning *ml, struct eth_addr src, ++ int vlan, bool is_gratuitous_arp, bool is_bond, ++ void *in_port) + OVS_EXCLUDED(ml->rwlock); ++#endif + + /* Lookup. */ + struct mac_entry *mac_learning_lookup(const struct mac_learning *ml, +diff --git a/openvswitch-2.14.2/lib/netdev-dpdk.c b/openvswitch-2.14.2/lib/netdev-dpdk.c +index f9284d0..858957c 100644 +--- a/openvswitch-2.14.2/lib/netdev-dpdk.c ++++ b/openvswitch-2.14.2/lib/netdev-dpdk.c +@@ -26,6 +26,10 @@ + #include + #include + ++#ifdef HAVE_HWOFF_AGENT ++#include "hwoff_init_func.h" ++#endif ++ + /* Include rte_compat.h first to allow experimental API's needed for the + * rte_meter.h rfc4115 functions. Once they are no longer marked as + * experimental the #define and rte_compat.h include can be removed. +@@ -74,6 +78,9 @@ + #include "userspace-tso.h" + #include "util.h" + #include "uuid.h" ++#ifdef HAVE_HWOFF_AGENT ++#include "hwoff_init_func.h" ++#endif + + enum {VIRTIO_RXQ, VIRTIO_TXQ, VIRTIO_QNUM}; + +@@ -109,7 +116,7 @@ COVERAGE_DEFINE(vhost_notification); + * enough hugepages) we keep halving the number until the allocation succeeds + * or we reach MIN_NB_MBUF */ + +-#define MAX_NB_MBUF (4096 * 64) ++#define MAX_NB_MBUF (512 * 320 * 2) // queue_desc * queue_num * rx/tx + #define MIN_NB_MBUF (4096 * 4) + #define MP_CACHE_SZ RTE_MEMPOOL_CACHE_MAX_SIZE + +@@ -187,6 +194,7 @@ static int vring_state_changed(int vid, uint16_t queue_id, int enable); + static void destroy_connection(int vid); + static void vhost_guest_notified(int vid); + ++#ifdef DPDK_2011_AND_BEFORE + static const struct vhost_device_ops virtio_net_device_ops = + { + .new_device = new_device, +@@ -197,6 +205,19 @@ static const struct vhost_device_ops virtio_net_device_ops = + .destroy_connection = destroy_connection, + .guest_notified = vhost_guest_notified, + }; ++#else ++static const struct rte_vhost_device_ops virtio_net_device_ops = ++{ ++ .new_device = new_device, ++ .destroy_device = destroy_device, ++ .vring_state_changed = vring_state_changed, ++ .features_changed = NULL, ++ .new_connection = NULL, ++ .destroy_connection = destroy_connection, ++ .guest_notified = vhost_guest_notified, ++}; ++#endif ++ + + /* Custom software stats for dpdk ports */ + struct netdev_dpdk_sw_stats { +@@ -432,6 +453,10 @@ struct netdev_dpdk { + /* If true, rte_eth_dev_start() was successfully called */ + bool started; + bool reset_needed; ++#ifdef HAVE_HWOFF_AGENT ++ bool hwoff_reconfigure; ++ bool hwoff_used_share_upcall; ++#endif + /* 1 pad byte here. */ + struct eth_addr hwaddr; + int mtu; +@@ -675,6 +700,7 @@ dpdk_calculate_mbufs(struct netdev_dpdk *dev, int mtu, bool per_port_mp) + return n_mbufs; + } + ++#define HWOFF_MBUF_SIZE 2176 + static struct dpdk_mp * + dpdk_mp_create(struct netdev_dpdk *dev, int mtu, bool per_port_mp) + { +@@ -699,8 +725,11 @@ dpdk_mp_create(struct netdev_dpdk *dev, int mtu, bool per_port_mp) + dmp->refcount = 1; + + /* Get the size of each mbuf, based on the MTU */ ++#ifdef HAVE_HWOFF_AGENT ++ mbuf_size = HWOFF_MBUF_SIZE; ++#else + mbuf_size = MTU_TO_FRAME_LEN(mtu); +- ++#endif + n_mbufs = dpdk_calculate_mbufs(dev, mtu, per_port_mp); + + do { +@@ -1146,8 +1175,13 @@ dpdk_eth_dev_init(struct netdev_dpdk *dev) + } + } + +- n_rxq = MIN(info.max_rx_queues, dev->up.n_rxq); + n_txq = MIN(info.max_tx_queues, dev->up.n_txq); ++ if(info.max_rx_queues < (dev->up.n_rxq)) { ++ n_rxq = info.max_rx_queues; ++ VLOG_INFO("option:n_rxq is out of range, it has changed to default maximum value:%d", info.max_rx_queues); ++ } else { ++ n_rxq = dev->up.n_rxq; ++ } + + diag = dpdk_eth_dev_port_config(dev, n_rxq, n_txq); + if (diag) { +@@ -1247,6 +1281,9 @@ common_construct(struct netdev *netdev, dpdk_port_t port_no, + dev->attached = false; + dev->started = false; + dev->reset_needed = false; ++#ifdef HAVE_HWOFF_AGENT ++ dev->hwoff_reconfigure = false; ++#endif + + ovsrcu_init(&dev->qos_conf, NULL); + +@@ -1285,31 +1322,11 @@ common_construct(struct netdev *netdev, dpdk_port_t port_no, + return 0; + } + +-/* Get the number of OVS interfaces which have the same DPDK +- * rte device (e.g. same pci bus address). +- * FIXME: avoid direct access to DPDK internal array rte_eth_devices. +- */ +-static int +-netdev_dpdk_get_num_ports(struct rte_device *device) +- OVS_REQUIRES(dpdk_mutex) +-{ +- struct netdev_dpdk *dev; +- int count = 0; +- +- LIST_FOR_EACH (dev, list_node, &dpdk_list) { +- if (rte_eth_devices[dev->port_id].device == device +- && rte_eth_devices[dev->port_id].state != RTE_ETH_DEV_UNUSED) { +- count++; +- } +- } +- return count; +-} +- + static int + vhost_common_construct(struct netdev *netdev) + OVS_REQUIRES(dpdk_mutex) + { +- int socket_id = rte_lcore_to_socket_id(rte_get_master_lcore()); ++ int socket_id = rte_lcore_to_socket_id(rte_get_main_lcore()); + struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); + + dev->vhost_rxq_enabled = dpdk_rte_mzalloc(OVS_VHOST_MAX_QUEUE_NUM * +@@ -1458,9 +1475,6 @@ static void + netdev_dpdk_destruct(struct netdev *netdev) + { + struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); +- struct rte_device *rte_dev; +- struct rte_eth_dev *eth_dev; +- bool remove_on_close; + + ovs_mutex_lock(&dpdk_mutex); + +@@ -1468,25 +1482,43 @@ netdev_dpdk_destruct(struct netdev *netdev) + dev->started = false; + + if (dev->attached) { +- /* Retrieve eth device data before closing it. +- * FIXME: avoid direct access to DPDK internal array rte_eth_devices. +- */ +- eth_dev = &rte_eth_devices[dev->port_id]; +- remove_on_close = +- eth_dev->data && +- (eth_dev->data->dev_flags & RTE_ETH_DEV_CLOSE_REMOVE); +- rte_dev = eth_dev->device; ++ bool dpdk_resources_still_used = false; ++ struct rte_eth_dev_info dev_info; ++ dpdk_port_t sibling_port_id; ++ ++ /* Check if this netdev has siblings (i.e. shares DPDK resources) among ++ * other OVS netdevs. */ ++ RTE_ETH_FOREACH_DEV_SIBLING (sibling_port_id, dev->port_id) { ++ struct netdev_dpdk *sibling; ++ ++ /* RTE_ETH_FOREACH_DEV_SIBLING lists dev->port_id as part of the ++ * loop. */ ++ if (sibling_port_id == dev->port_id) { ++ continue; ++ } ++ LIST_FOR_EACH (sibling, list_node, &dpdk_list) { ++ if (sibling->port_id != sibling_port_id) { ++ continue; ++ } ++ dpdk_resources_still_used = true; ++ break; ++ } ++ if (dpdk_resources_still_used) { ++ break; ++ } ++ } ++ ++ /* Retrieve eth device data before closing it. */ ++ rte_eth_dev_info_get(dev->port_id, &dev_info); + + /* Remove the eth device. */ + rte_eth_dev_close(dev->port_id); + +- /* Remove this rte device and all its eth devices if flag +- * RTE_ETH_DEV_CLOSE_REMOVE is not supported (which means representors +- * are not supported), or if all the eth devices belonging to the rte +- * device are closed. +- */ +- if (!remove_on_close || !netdev_dpdk_get_num_ports(rte_dev)) { +- int ret = rte_dev_remove(rte_dev); ++ /* Remove the rte device if no associated eth device is used by OVS. ++ * Note: any remaining eth devices associated to this rte device are ++ * closed by DPDK ethdev layer. */ ++ if (!dpdk_resources_still_used) { ++ int ret = rte_dev_remove(dev_info.device); + + if (ret < 0) { + VLOG_ERR("Device '%s' can not be detached: %s.", +@@ -1787,6 +1819,103 @@ static dpdk_port_t netdev_dpdk_get_port_by_devargs(const char *devargs) + return port_id; + } + ++#ifdef HAVE_HWOFF_AGENT ++#define HWOFF_SPLIT_STR_LENGTH 30 ++int ++hwoff_netdev_name_get(uint16_t port_id, char name_buffer[], int buffer_size) ++{ ++ struct netdev_dpdk *dev = NULL; ++ dev = netdev_dpdk_lookup_by_port_id(port_id); ++ if (dev == NULL) { ++ return -1; ++ } ++ ovs_strlcpy(name_buffer, dev->up.name, buffer_size); ++ return 0; ++} ++ ++bool ++hwoff_netdev_is_shared(struct netdev *dev) ++{ ++ if (!is_dpdk_class(dev->netdev_class)) { ++ return false; ++ } ++ ++ struct netdev_dpdk *dpdk_dev = NULL; ++ dpdk_dev = netdev_dpdk_cast(dev); ++ return dpdk_dev == NULL ? false : dpdk_dev->hwoff_used_share_upcall; ++} ++ ++int ++open_share_upcall(const char *key, const char *value, void *extra_args) ++{ ++ uint8_t *share_upcall = (uint8_t *)extra_args; ++ if (value == NULL || share_upcall == NULL) { ++ return -1; ++ } ++ ++ if (strcmp("true", value) == 0) { ++ *share_upcall = true; ++ } else if (strcmp("false", value) == 0) { ++ *share_upcall = false; ++ } else { ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int ++hwoff_get_share_upcall_str(const char *src, char *dst) ++{ ++ char *sub_str = strstr(src ,"share-upcall-queues"); ++ if (sub_str == NULL) { ++ return -1; ++ } ++ ++ for (int i = 0; i < HWOFF_SPLIT_STR_LENGTH; ++i) { ++ if (sub_str[i] == '\0') { ++ dst[i] = sub_str[i]; ++ return 0; ++ } ++ ++ if (sub_str[i] == ',') { ++ return 0; ++ } ++ dst[i] = sub_str[i]; ++ } ++ ++ return 0; ++} ++ ++static bool hwoff_port_share_upcall(const char *args) ++{ ++ const char *valid_key_list[] = {"share-upcall-queues", NULL}; ++ struct rte_kvargs *kvargs = NULL; ++ int ret = 0; ++ bool share_upcall = false; ++ char share_upcall_str[HWOFF_SPLIT_STR_LENGTH] = {0}; ++ ++ ret = hwoff_get_share_upcall_str(args, share_upcall_str); ++ if (ret < 0) { ++ return false; ++ } ++ ++ kvargs = rte_kvargs_parse(share_upcall_str, valid_key_list); ++ if (kvargs == NULL) { ++ return false; ++ } ++ ++ ret = rte_kvargs_process(kvargs, "share-upcall-queues", open_share_upcall, &share_upcall); ++ if (ret != 0) { ++ rte_kvargs_free(kvargs); ++ return false; ++ } ++ ++ rte_kvargs_free(kvargs); ++ return share_upcall; ++} ++#endif ++ + /* + * Normally, a PCI id (optionally followed by a representor number) + * is enough for identifying a specific DPDK port. +@@ -1826,6 +1955,13 @@ netdev_dpdk_process_devargs(struct netdev_dpdk *dev, + } + } + } ++#ifdef HAVE_HWOFF_AGENT ++ /* Add share upcall queue flag on dpdk_device according to input args */ ++ if (new_port_id != DPDK_ETH_PORT_ID_INVALID && ++ hwoff_port_share_upcall(devargs)) { ++ dev->hwoff_used_share_upcall = true; ++ } ++#endif + + if (new_port_id == DPDK_ETH_PORT_ID_INVALID) { + VLOG_WARN_BUF(errp, "Error attaching device '%s' to DPDK", devargs); +@@ -1868,7 +2004,12 @@ dpdk_set_rxq_config(struct netdev_dpdk *dev, const struct smap *args) + { + int new_n_rxq; + +- new_n_rxq = MAX(smap_get_int(args, "n_rxq", NR_QUEUE), 1); ++ if(smap_get_int(args, "n_rxq", NR_QUEUE) < 1) { ++ new_n_rxq = 1; ++ VLOG_INFO("option:n_rxq is out of range, it has changed to default minimum value:%d", new_n_rxq); ++ } else { ++ new_n_rxq = smap_get_int(args, "n_rxq", NR_QUEUE); ++ } + if (new_n_rxq != dev->requested_n_rxq) { + dev->requested_n_rxq = new_n_rxq; + netdev_request_reconfigure(&dev->up); +@@ -1974,7 +2115,14 @@ netdev_dpdk_set_config(struct netdev *netdev, const struct smap *args, + if (err) { + goto out; + } +- ++#ifdef HAVE_HWOFF_AGENT ++ hwoff_func* funcs = hwoff_get_funcs(); ++ err = funcs->hwoff_parse_vf_extra_options(dev->port_id, args); ++ if (err == 0) { ++ dev->hwoff_reconfigure = true; ++ netdev_request_reconfigure(netdev); ++ } ++#endif + lsc_interrupt_mode = smap_get_bool(args, "dpdk-lsc-interrupt", false); + if (dev->requested_lsc_interrupt_mode != lsc_interrupt_mode) { + dev->requested_lsc_interrupt_mode = lsc_interrupt_mode; +@@ -2039,12 +2187,6 @@ netdev_dpdk_vhost_client_set_config(struct netdev *netdev, + if (!nullable_string_is_equal(path, dev->vhost_id)) { + free(dev->vhost_id); + dev->vhost_id = nullable_xstrdup(path); +- /* check zero copy configuration */ +- if (smap_get_bool(args, "dq-zero-copy", false)) { +- dev->vhost_driver_flags |= RTE_VHOST_USER_DEQUEUE_ZERO_COPY; +- } else { +- dev->vhost_driver_flags &= ~RTE_VHOST_USER_DEQUEUE_ZERO_COPY; +- } + netdev_request_reconfigure(netdev); + } + } +@@ -2145,14 +2287,21 @@ netdev_dpdk_prep_hwol_packet(struct netdev_dpdk *dev, struct rte_mbuf *mbuf) + { + struct dp_packet *pkt = CONTAINER_OF(mbuf, struct dp_packet, mbuf); + ++#ifdef DPDK_2011_AND_BEFORE + if (mbuf->ol_flags & PKT_TX_L4_MASK) { ++#else ++ if (mbuf->ol_flags & RTE_MBUF_F_TX_L4_MASK) { ++#endif + mbuf->l2_len = (char *)dp_packet_l3(pkt) - (char *)dp_packet_eth(pkt); + mbuf->l3_len = (char *)dp_packet_l4(pkt) - (char *)dp_packet_l3(pkt); + mbuf->outer_l2_len = 0; + mbuf->outer_l3_len = 0; + } +- ++#ifdef DPDK_2011_AND_BEFORE + if (mbuf->ol_flags & PKT_TX_TCP_SEG) { ++#else ++ if (mbuf->ol_flags & RTE_MBUF_F_TX_TCP_SEG) { ++#endif + struct tcp_header *th = dp_packet_l4(pkt); + + if (!th) { +@@ -2162,12 +2311,19 @@ netdev_dpdk_prep_hwol_packet(struct netdev_dpdk *dev, struct rte_mbuf *mbuf) + } + + mbuf->l4_len = TCP_OFFSET(th->tcp_ctl) * 4; ++#ifdef DPDK_2011_AND_BEFORE + mbuf->ol_flags |= PKT_TX_TCP_CKSUM; + mbuf->tso_segsz = dev->mtu - mbuf->l3_len - mbuf->l4_len; +- + if (mbuf->ol_flags & PKT_TX_IPV4) { + mbuf->ol_flags |= PKT_TX_IP_CKSUM; + } ++#else ++ mbuf->ol_flags |= RTE_MBUF_F_TX_TCP_CKSUM; ++ mbuf->tso_segsz = dev->mtu - mbuf->l3_len - mbuf->l4_len; ++ if (mbuf->ol_flags & RTE_MBUF_F_TX_IPV4) { ++ mbuf->ol_flags |= RTE_MBUF_F_TX_IP_CKSUM; ++ } ++#endif + } + return true; + } +@@ -2513,7 +2669,11 @@ netdev_dpdk_filter_packet_len(struct netdev_dpdk *dev, struct rte_mbuf **pkts, + for (i = 0; i < pkt_cnt; i++) { + pkt = pkts[i]; + if (OVS_UNLIKELY((pkt->pkt_len > dev->max_packet_len) ++#ifdef DPDK_2011_AND_BEFORE + && !(pkt->ol_flags & PKT_TX_TCP_SEG))) { ++#else ++ && !(pkt->ol_flags & RTE_MBUF_F_TX_TCP_SEG))) { ++#endif + VLOG_WARN_RL(&rl, "%s: Too big size %" PRIu32 " " + "max_packet_len %d", dev->up.name, pkt->pkt_len, + dev->max_packet_len); +@@ -2734,13 +2894,19 @@ dpdk_copy_dp_packet_to_mbuf(struct rte_mempool *mp, struct dp_packet *pkt_orig) + + mbuf_dest->tx_offload = pkt_orig->mbuf.tx_offload; + mbuf_dest->packet_type = pkt_orig->mbuf.packet_type; ++#ifdef DPDK_2011_AND_BEFORE + mbuf_dest->ol_flags |= (pkt_orig->mbuf.ol_flags & + ~(EXT_ATTACHED_MBUF | IND_ATTACHED_MBUF)); +- + memcpy(&pkt_dest->l2_pad_size, &pkt_orig->l2_pad_size, + sizeof(struct dp_packet) - offsetof(struct dp_packet, l2_pad_size)); +- + if (mbuf_dest->ol_flags & PKT_TX_L4_MASK) { ++#else ++ mbuf_dest->ol_flags |= (pkt_orig->mbuf.ol_flags & ++ ~(RTE_MBUF_F_EXTERNAL | RTE_MBUF_F_INDIRECT)); ++ memcpy(&pkt_dest->l2_pad_size, &pkt_orig->l2_pad_size, ++ sizeof(struct dp_packet) - offsetof(struct dp_packet, l2_pad_size)); ++ if (mbuf_dest->ol_flags & RTE_MBUF_F_TX_L4_MASK) { ++#endif + mbuf_dest->l2_len = (char *)dp_packet_l3(pkt_dest) + - (char *)dp_packet_eth(pkt_dest); + mbuf_dest->l3_len = (char *)dp_packet_l4(pkt_dest) +@@ -2785,7 +2951,11 @@ dpdk_do_tx_copy(struct netdev *netdev, int qid, struct dp_packet_batch *batch) + uint32_t size = dp_packet_size(packet); + + if (size > dev->max_packet_len ++#ifdef DPDK_2011_AND_BEFORE + && !(packet->mbuf.ol_flags & PKT_TX_TCP_SEG)) { ++#else ++ && !(packet->mbuf.ol_flags & RTE_MBUF_F_TX_TCP_SEG)) { ++#endif + VLOG_WARN_RL(&rl, "Too big size %u max_packet_len %d", size, + dev->max_packet_len); + mtu_drops++; +@@ -3353,8 +3523,29 @@ netdev_dpdk_set_policing(struct netdev* netdev, uint32_t policer_rate, + : !policer_burst ? 8000 + : policer_burst); + ++#if HAVE_HWOFF_AGENT ++ bool eth_flag = false; ++ hwoff_func* funcs = hwoff_get_funcs(); ++ /* This function obtains dev->mutex, which conflicts with the following open source lock obtaining. ++ * Therefore, the function needs to be split into two parts. ++ */ ++ if (funcs->hwoff_is_ethdev(netdev)) { ++ eth_flag = true; ++ } ++#endif ++ + ovs_mutex_lock(&dev->mutex); + ++#if HAVE_HWOFF_AGENT ++ if (eth_flag) { ++ if (funcs->hwoff_set_ingress_policing) { ++ funcs->hwoff_set_ingress_policing(dev->port_id, policer_rate, policer_burst); ++ } ++ ovs_mutex_unlock(&dev->mutex); ++ return 0; ++ } ++#endif ++ + policer = ovsrcu_get_protected(struct ingress_policer *, + &dev->ingress_policer); + +@@ -3629,8 +3820,13 @@ netdev_dpdk_get_status(const struct netdev *netdev, struct smap *args) + ovs_mutex_unlock(&dev->mutex); + const struct rte_bus *bus; + const struct rte_pci_device *pci_dev; ++#ifdef DPDK_2011_AND_BEFORE + uint16_t vendor_id = PCI_ANY_ID; + uint16_t device_id = PCI_ANY_ID; ++#else ++ uint16_t vendor_id = RTE_PCI_ANY_ID; ++ uint16_t device_id = RTE_PCI_ANY_ID; ++#endif + bus = rte_bus_find_by_device(dev_info.device); + if (bus && !strcmp(bus->name, "pci")) { + pci_dev = RTE_DEV_TO_PCI(dev_info.device); +@@ -3738,12 +3934,12 @@ static void + netdev_dpdk_detach(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[], void *aux OVS_UNUSED) + { +- char *response; +- dpdk_port_t port_id; +- struct netdev_dpdk *dev; +- struct rte_device *rte_dev; + struct ds used_interfaces = DS_EMPTY_INITIALIZER; ++ struct rte_eth_dev_info dev_info; ++ dpdk_port_t sibling_port_id; ++ dpdk_port_t port_id; + bool used = false; ++ char *response; + + ovs_mutex_lock(&dpdk_mutex); + +@@ -3753,18 +3949,21 @@ netdev_dpdk_detach(struct unixctl_conn *conn, int argc OVS_UNUSED, + goto error; + } + +- rte_dev = rte_eth_devices[port_id].device; + ds_put_format(&used_interfaces, + "Device '%s' is being used by the following interfaces:", + argv[1]); + +- LIST_FOR_EACH (dev, list_node, &dpdk_list) { +- /* FIXME: avoid direct access to DPDK array rte_eth_devices. */ +- if (rte_eth_devices[dev->port_id].device == rte_dev +- && rte_eth_devices[dev->port_id].state != RTE_ETH_DEV_UNUSED) { ++ RTE_ETH_FOREACH_DEV_SIBLING (sibling_port_id, port_id) { ++ struct netdev_dpdk *dev; ++ ++ LIST_FOR_EACH (dev, list_node, &dpdk_list) { ++ if (dev->port_id != sibling_port_id) { ++ continue; ++ } + used = true; + ds_put_format(&used_interfaces, " %s", + netdev_get_name(&dev->up)); ++ break; + } + } + +@@ -3776,8 +3975,9 @@ netdev_dpdk_detach(struct unixctl_conn *conn, int argc OVS_UNUSED, + } + ds_destroy(&used_interfaces); + ++ rte_eth_dev_info_get(port_id, &dev_info); + rte_eth_dev_close(port_id); +- if (rte_dev_remove(rte_dev) < 0) { ++ if (rte_dev_remove(dev_info.device) < 0) { + response = xasprintf("Device '%s' can not be detached", argv[1]); + goto error; + } +@@ -4277,8 +4477,29 @@ netdev_dpdk_set_qos(struct netdev *netdev, const char *type, + struct qos_conf *qos_conf, *new_qos_conf = NULL; + int error = 0; + ++#if HAVE_HWOFF_AGENT ++ bool eth_flag = false; ++ hwoff_func* funcs = hwoff_get_funcs(); ++ /* This function obtains dev->mutex, which conflicts with the following open source lock obtaining. ++ * Therefore, the function needs to be split into two parts. ++ */ ++ if (funcs->hwoff_is_ethdev(netdev)) { ++ eth_flag = true; ++ } ++#endif ++ + ovs_mutex_lock(&dev->mutex); + ++#if HAVE_HWOFF_AGENT ++ if (eth_flag) { ++ if (funcs->hwoff_set_qos) { ++ funcs->hwoff_set_qos(dev->port_id, type, details); ++ } ++ ovs_mutex_unlock(&dev->mutex); ++ return error; ++ } ++#endif ++ + qos_conf = ovsrcu_get_protected(struct qos_conf *, &dev->qos_conf); + + new_ops = qos_lookup_name(type); +@@ -4910,7 +5131,12 @@ netdev_dpdk_reconfigure(struct netdev *netdev) + && dev->rxq_size == dev->requested_rxq_size + && dev->txq_size == dev->requested_txq_size + && dev->socket_id == dev->requested_socket_id ++#ifdef HAVE_HWOFF_AGENT ++ && dev->started && !dev->reset_needed ++ && !dev->hwoff_reconfigure) { ++#else + && dev->started && !dev->reset_needed) { ++#endif + /* Reconfiguration is unnecessary */ + + goto out; +@@ -5025,7 +5251,6 @@ netdev_dpdk_vhost_client_reconfigure(struct netdev *netdev) + int err; + uint64_t vhost_flags = 0; + uint64_t vhost_unsup_flags; +- bool zc_enabled; + + ovs_mutex_lock(&dev->mutex); + +@@ -5051,19 +5276,6 @@ netdev_dpdk_vhost_client_reconfigure(struct netdev *netdev) + vhost_flags |= RTE_VHOST_USER_POSTCOPY_SUPPORT; + } + +- zc_enabled = dev->vhost_driver_flags +- & RTE_VHOST_USER_DEQUEUE_ZERO_COPY; +- /* Enable zero copy flag, if requested */ +- if (zc_enabled) { +- vhost_flags |= RTE_VHOST_USER_DEQUEUE_ZERO_COPY; +- /* DPDK vHost library doesn't allow zero-copy with linear buffers. +- * Hence disable Linear buffer. +- */ +- vhost_flags &= ~RTE_VHOST_USER_LINEARBUF_SUPPORT; +- VLOG_WARN("Zero copy enabled, disabling linear buffer" +- " check for vHost port %s", dev->up.name); +- } +- + /* Enable External Buffers if TCP Segmentation Offload is enabled. */ + if (userspace_tso_enabled()) { + vhost_flags |= RTE_VHOST_USER_EXTBUF_SUPPORT; +@@ -5080,11 +5292,6 @@ netdev_dpdk_vhost_client_reconfigure(struct netdev *netdev) + VLOG_INFO("vHost User device '%s' created in 'client' mode, " + "using client socket '%s'", + dev->up.name, dev->vhost_id); +- if (zc_enabled) { +- VLOG_INFO("Zero copy enabled for vHost port %s", dev->up.name); +- VLOG_WARN("Zero copy support is deprecated and will be " +- "removed in the next OVS release."); +- } + } + + err = rte_vhost_driver_callback_register(dev->vhost_id, +@@ -5145,9 +5352,7 @@ netdev_dpdk_get_port_id(struct netdev *netdev) + } + + dev = netdev_dpdk_cast(netdev); +- ovs_mutex_lock(&dev->mutex); + ret = dev->port_id; +- ovs_mutex_unlock(&dev->mutex); + out: + return ret; + } +@@ -5162,6 +5367,14 @@ netdev_dpdk_flow_api_supported(struct netdev *netdev) + goto out; + } + ++#ifdef HAVE_HWOFF_AGENT ++ hwoff_func* funcs = hwoff_get_funcs(); ++ bool flag = funcs->hwoff_is_ethdev(netdev); ++ if (flag == true) { ++ return false; ++ } ++#endif ++ + dev = netdev_dpdk_cast(netdev); + ovs_mutex_lock(&dev->mutex); + if (dev->type == DPDK_DEV_ETH) { +@@ -5181,9 +5394,7 @@ netdev_dpdk_rte_flow_destroy(struct netdev *netdev, + struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); + int ret; + +- ovs_mutex_lock(&dev->mutex); + ret = rte_flow_destroy(dev->port_id, rte_flow, error); +- ovs_mutex_unlock(&dev->mutex); + return ret; + } + +@@ -5197,9 +5408,7 @@ netdev_dpdk_rte_flow_create(struct netdev *netdev, + struct rte_flow *flow; + struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); + +- ovs_mutex_lock(&dev->mutex); + flow = rte_flow_create(dev->port_id, attr, items, actions, error); +- ovs_mutex_unlock(&dev->mutex); + return flow; + } + +@@ -5209,7 +5418,7 @@ netdev_dpdk_rte_flow_query_count(struct netdev *netdev, + struct rte_flow_query_count *query, + struct rte_flow_error *error) + { +- struct rte_flow_action_count count = { .shared = 0, .id = 0 }; ++ struct rte_flow_action_count count = { .id = 0, }; + const struct rte_flow_action actions[] = { + { + .type = RTE_FLOW_ACTION_TYPE_COUNT, +@@ -5227,9 +5436,7 @@ netdev_dpdk_rte_flow_query_count(struct netdev *netdev, + } + + dev = netdev_dpdk_cast(netdev); +- ovs_mutex_lock(&dev->mutex); + ret = rte_flow_query(dev->port_id, rte_flow, actions, query, error); +- ovs_mutex_unlock(&dev->mutex); + return ret; + } + +diff --git a/openvswitch-2.14.2/lib/netdev-dpdk.h b/openvswitch-2.14.2/lib/netdev-dpdk.h +index 848346c..6853aeb 100644 +--- a/openvswitch-2.14.2/lib/netdev-dpdk.h ++++ b/openvswitch-2.14.2/lib/netdev-dpdk.h +@@ -20,6 +20,9 @@ + #include + + #include "openvswitch/compiler.h" ++#ifdef HAVE_HWOFF_AGENT ++#include ++#endif + + struct dp_packet; + struct netdev; +@@ -56,6 +59,14 @@ netdev_dpdk_rte_flow_query_count(struct netdev *netdev, + int + netdev_dpdk_get_port_id(struct netdev *netdev); + ++#ifdef HAVE_HWOFF_AGENT ++int ++hwoff_netdev_name_get(uint16_t port_id, char name_buffer[], int buffer_size); ++ ++bool ++hwoff_netdev_is_shared(struct netdev *dev); ++#endif ++ + #else + + static inline void +diff --git a/openvswitch-2.14.2/lib/netdev-native-tnl.c b/openvswitch-2.14.2/lib/netdev-native-tnl.c +index b89dfdd..0b3a745 100644 +--- a/openvswitch-2.14.2/lib/netdev-native-tnl.c ++++ b/openvswitch-2.14.2/lib/netdev-native-tnl.c +@@ -44,6 +44,9 @@ + #include "unaligned.h" + #include "unixctl.h" + #include "openvswitch/vlog.h" ++#ifdef HAVE_HWOFF_AGENT ++#include "hwoff_init_func.h" ++#endif + + VLOG_DEFINE_THIS_MODULE(native_tnl); + static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(60, 5); +@@ -248,7 +251,16 @@ netdev_tnl_push_udp_header(const struct netdev *netdev OVS_UNUSED, + udp = netdev_tnl_push_ip_header(packet, data->header, data->header_len, &ip_tot_size); + + /* set udp src port */ ++#ifdef HAVE_HWOFF_AGENT ++ hwoff_func* funcs = hwoff_get_funcs(); ++ if (funcs->hwoff_tnl_get_src_port) { ++ udp->udp_src = funcs->hwoff_tnl_get_src_port(packet); ++ } else { ++ udp->udp_src = netdev_tnl_get_src_port(packet); ++ } ++#else + udp->udp_src = netdev_tnl_get_src_port(packet); ++#endif + udp->udp_len = htons(ip_tot_size); + + if (udp->udp_csum) { +diff --git a/openvswitch-2.14.2/lib/netdev-offload-dpdk.c b/openvswitch-2.14.2/lib/netdev-offload-dpdk.c +index 17b08ca..704ea18 100644 +--- a/openvswitch-2.14.2/lib/netdev-offload-dpdk.c ++++ b/openvswitch-2.14.2/lib/netdev-offload-dpdk.c +@@ -28,6 +28,11 @@ + #include "openvswitch/vlog.h" + #include "packets.h" + #include "uuid.h" ++#ifdef HAVE_HWOFF_AGENT ++#include "odp-util.h" ++#include "unixctl.h" ++#include "hwoff_init_func.h" ++#endif + + VLOG_DEFINE_THIS_MODULE(netdev_offload_dpdk); + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(100, 5); +@@ -58,6 +63,7 @@ struct ufid_to_rte_flow_data { + struct cmap_node node; + ovs_u128 ufid; + struct rte_flow *rte_flow; ++ int ref_cnt; + bool actions_offloaded; + struct dpif_flow_stats stats; + }; +@@ -1017,7 +1023,20 @@ add_port_id_action(struct flow_actions *actions, + struct rte_flow_action_port_id *port_id; + int outdev_id; + ++#ifdef HAVE_HWOFF_AGENT ++ hwoff_func* funcs = hwoff_get_funcs(); ++ if (funcs->hwoff_is_hiovs_netdev && funcs->hwoff_is_hiovs_netdev(outdev)) { ++ if (funcs->hwoff_is_ethdev(outdev)) { ++ outdev_id = funcs->hwoff_get_eth_vport_id(outdev); ++ } else { ++ outdev_id = outdev->netdev_class->get_ifindex(outdev); ++ } ++ } else { ++ outdev_id = netdev_dpdk_get_port_id(outdev); ++ } ++#else + outdev_id = netdev_dpdk_get_port_id(outdev); ++#endif + if (outdev_id < 0) { + return -1; + } +@@ -1030,14 +1049,19 @@ add_port_id_action(struct flow_actions *actions, + static int + add_output_action(struct netdev *netdev, + struct flow_actions *actions, +- const struct nlattr *nla) ++ const struct nlattr *nla, ++ void *pmd) + { + struct netdev *outdev; + odp_port_t port; + int ret = 0; + + port = nl_attr_get_odp_port(nla); +- outdev = netdev_ports_get(port, netdev->dpif_type); ++ if (pmd) { ++ outdev = dp_get_outdev_from_pmd(port, pmd); ++ } else { ++ outdev = netdev_ports_get(port, netdev->dpif_type); ++ } + if (outdev == NULL) { + VLOG_DBG_RL(&rl, "Cannot find netdev for odp port %"PRIu32, port); + return -1; +@@ -1048,7 +1072,10 @@ add_output_action(struct netdev *netdev, + netdev_get_name(netdev), netdev_get_name(outdev)); + ret = -1; + } +- netdev_close(outdev); ++ ++ if (pmd == NULL) { ++ netdev_close(outdev); ++ } + return ret; + } + +@@ -1076,10 +1103,12 @@ add_set_flow_action__(struct flow_actions *actions, + memcpy(spec, value, size); + add_flow_action(actions, attr, spec); + ++#ifndef HAVE_HWOFF_AGENT + /* Clear used mask for later checking. */ + if (mask) { + memset(mask, 0, size); + } ++#endif + return 0; + } + +@@ -1108,6 +1137,104 @@ BUILD_ASSERT_DECL(sizeof(struct rte_flow_action_set_tp) == + BUILD_ASSERT_DECL(sizeof(struct rte_flow_action_set_tp) == + MEMBER_SIZEOF(struct ovs_key_udp, udp_dst)); + ++#ifdef HAVE_HWOFF_AGENT ++static int ++parse_set_actions(struct flow_actions *actions, ++ const struct nlattr *set_actions, ++ const size_t set_actions_len, ++ bool masked) ++{ ++ const struct nlattr *sa; ++ unsigned int sleft; ++ ++#define add_set_flow_action(field, type) \ ++ if (add_set_flow_action__(actions, &key->field, \ ++ mask ? CONST_CAST(void *, &mask->field) : NULL, \ ++ sizeof key->field, type)) { \ ++ return -1; \ ++ } ++ ++ NL_ATTR_FOR_EACH_UNSAFE (sa, sleft, set_actions, set_actions_len) { ++ if (nl_attr_type(sa) == OVS_KEY_ATTR_ETHERNET) { ++ const struct ovs_key_ethernet *key = nl_attr_get(sa); ++ const struct ovs_key_ethernet *mask = masked ? key + 1 : NULL; ++ ++ add_set_flow_action(eth_src, RTE_FLOW_ACTION_TYPE_SET_MAC_SRC); ++ add_set_flow_action(eth_dst, RTE_FLOW_ACTION_TYPE_SET_MAC_DST); ++ } else if (nl_attr_type(sa) == OVS_KEY_ATTR_IPV4) { ++ const struct ovs_key_ipv4 *key = nl_attr_get(sa); ++ const struct ovs_key_ipv4 *mask = masked ? key + 1 : NULL; ++ ++ add_set_flow_action(ipv4_src, RTE_FLOW_ACTION_TYPE_SET_IPV4_SRC); ++ add_set_flow_action(ipv4_dst, RTE_FLOW_ACTION_TYPE_SET_IPV4_DST); ++ add_set_flow_action(ipv4_ttl, RTE_FLOW_ACTION_TYPE_SET_TTL); ++ } else if (nl_attr_type(sa) == OVS_KEY_ATTR_IPV6) { ++ const struct ovs_key_ipv6 *key = nl_attr_get(sa); ++ const struct ovs_key_ipv6 *mask = masked ? key + 1 : NULL; ++ ++ add_set_flow_action(ipv6_src, RTE_FLOW_ACTION_TYPE_SET_IPV6_SRC); ++ add_set_flow_action(ipv6_dst, RTE_FLOW_ACTION_TYPE_SET_IPV6_DST); ++ add_set_flow_action(ipv6_hlimit, RTE_FLOW_ACTION_TYPE_SET_TTL); ++ } else if (nl_attr_type(sa) == OVS_KEY_ATTR_TCP) { ++ const struct ovs_key_tcp *key = nl_attr_get(sa); ++ const struct ovs_key_tcp *mask = masked ? key + 1 : NULL; ++ ++ add_set_flow_action(tcp_src, RTE_FLOW_ACTION_TYPE_SET_TP_SRC); ++ add_set_flow_action(tcp_dst, RTE_FLOW_ACTION_TYPE_SET_TP_DST); ++ } else if (nl_attr_type(sa) == OVS_KEY_ATTR_UDP) { ++ const struct ovs_key_udp *key = nl_attr_get(sa); ++ const struct ovs_key_udp *mask = masked ? key + 1 : NULL; ++ ++ add_set_flow_action(udp_src, RTE_FLOW_ACTION_TYPE_SET_TP_SRC); ++ add_set_flow_action(udp_dst, RTE_FLOW_ACTION_TYPE_SET_TP_DST); ++ } else { ++ VLOG_DBG_RL(&rl, ++ "Unsupported set action type %d", nl_attr_type(sa)); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++static void add_vlan_to_vxlan_action(const struct nlattr *ca, struct flow_actions *actions) ++{ ++ struct rte_flow_action *real_actions = actions->actions; ++ struct rte_flow_action *one_act = NULL; ++ struct rte_flow_action *dst_act = NULL; ++ ++ one_act = real_actions; ++ while (one_act && (one_act->type != RTE_FLOW_ACTION_TYPE_END)) { ++ if (one_act->type != RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP) { ++ one_act = one_act + 1; ++ continue; ++ } ++ ++ dst_act = one_act; ++ break; ++ } ++ ++ if (dst_act == NULL) { ++ return; ++ } ++ ++ struct rte_flow_action_vxlan_encap *vxlan_info = (struct rte_flow_action_vxlan_encap *)dst_act->conf; ++ struct rte_flow_item *item = vxlan_info->definition; ++ ++ while (item->type != RTE_FLOW_ITEM_TYPE_END) { ++ item = item + 1; ++ } ++ ++ item->type = RTE_FLOW_ITEM_TYPE_VLAN; ++ const struct ovs_action_push_vlan *vlan_push = nl_attr_get(ca); ++ item->spec = &vlan_push->vlan_tci; ++ item->mask = NULL; ++ ++ item = item + 1; ++ item->type = RTE_FLOW_ITEM_TYPE_END; ++ return; ++} ++#else + static int + parse_set_actions(struct flow_actions *actions, + const struct nlattr *set_actions, +@@ -1191,11 +1318,12 @@ parse_set_actions(struct flow_actions *actions, + + return 0; + } ++#endif + + /* Maximum number of items in struct rte_flow_action_vxlan_encap. +- * ETH / IPv4(6) / UDP / VXLAN / END ++ * ETH / IPv4(6) / UDP / VXLAN / vlan /END + */ +-#define ACTION_VXLAN_ENCAP_ITEMS_NUM 5 ++#define ACTION_VXLAN_ENCAP_ITEMS_NUM 6 + + static int + add_vxlan_encap_action(struct flow_actions *actions, +@@ -1305,7 +1433,8 @@ static int + parse_clone_actions(struct netdev *netdev, + struct flow_actions *actions, + const struct nlattr *clone_actions, +- const size_t clone_actions_len) ++ const size_t clone_actions_len, ++ void *pmd) + { + const struct nlattr *ca; + unsigned int cleft; +@@ -1330,9 +1459,17 @@ parse_clone_actions(struct netdev *netdev, + add_flow_action(actions, RTE_FLOW_ACTION_TYPE_RAW_ENCAP, + raw_encap); + } else if (clone_type == OVS_ACTION_ATTR_OUTPUT) { +- if (add_output_action(netdev, actions, ca)) { ++ if (add_output_action(netdev, actions, ca, pmd)) { + return -1; + } ++#ifdef HAVE_HWOFF_AGENT ++ } else if (clone_type == OVS_ACTION_ATTR_PUSH_VLAN) { ++ add_vlan_to_vxlan_action(ca, actions); ++ } else if (clone_type == OVS_ACTION_ATTR_CT) { ++ add_flow_action(actions, RTE_FLOW_ACTION_TYPE_CT, NULL); ++ } else if (clone_type == OVS_ACTION_ATTR_RECIRC) { ++ add_flow_action(actions, RTE_FLOW_ACTION_TYPE_RECIRC, NULL); ++#endif + } else { + VLOG_DBG_RL(&rl, + "Unsupported nested action inside clone(), " +@@ -1347,19 +1484,29 @@ static int + parse_flow_actions(struct netdev *netdev, + struct flow_actions *actions, + struct nlattr *nl_actions, +- size_t nl_actions_len) ++ size_t nl_actions_len, ++ void *pmd) + { ++ bool have_hard_output = false; + struct nlattr *nla; + size_t left; + + add_count_action(actions); + NL_ATTR_FOR_EACH_UNSAFE (nla, left, nl_actions, nl_actions_len) { + if (nl_attr_type(nla) == OVS_ACTION_ATTR_OUTPUT) { +- if (add_output_action(netdev, actions, nla)) { +- return -1; ++ if (add_output_action(netdev, actions, nla, pmd)) { ++ /* to support vxlan and set action both modify ++ * set action will output tap port which don't supoort offload, ++ * then return -1. ++ * continue to use vxlan output port. ++ */ ++ continue; ++ } else { ++ have_hard_output = true; + } + } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_DROP) { + add_flow_action(actions, RTE_FLOW_ACTION_TYPE_DROP, NULL); ++ have_hard_output = true; + } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_SET || + nl_attr_type(nla) == OVS_ACTION_ATTR_SET_MASKED) { + const struct nlattr *set_actions = nl_attr_get(nla); +@@ -1378,14 +1525,41 @@ parse_flow_actions(struct netdev *netdev, + } + } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_POP_VLAN) { + add_flow_action(actions, RTE_FLOW_ACTION_TYPE_OF_POP_VLAN, NULL); ++#ifdef HAVE_HWOFF_AGENT ++ } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_TUNNEL_POP) { ++ odp_port_t port = nl_attr_get_odp_port(nla); ++ struct netdev *vport = netdev_ports_get(port, netdev->dpif_type); ++ if (!vport) { ++ continue; ++ } ++ if (!strcmp(netdev_get_type(vport), "vxlan")) { ++ /* if exists tunnel_pop action, it should be the first action */ ++ free_flow_actions(actions); ++ add_flow_action(actions, RTE_FLOW_ACTION_TYPE_VXLAN_DECAP, NULL); ++ } ++ netdev_close(vport); ++ have_hard_output = true; ++ } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_CT) { ++ add_flow_action(actions, RTE_FLOW_ACTION_TYPE_CT, NULL); ++ } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_RECIRC) { ++ add_flow_action(actions, RTE_FLOW_ACTION_TYPE_RECIRC, NULL); ++#endif ++#ifdef HAVE_HWOFF_AGENT ++ } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_CLONE) { ++/* if there is multi output, clone will not be the last atcion, so left would be longer than nla->nla_len, ++ we let it go here, offload will fail in agent because multi output. ++*/ ++#else + } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_CLONE && + left <= NLA_ALIGN(nla->nla_len)) { ++#endif + const struct nlattr *clone_actions = nl_attr_get(nla); + size_t clone_actions_len = nl_attr_get_size(nla); +- + if (parse_clone_actions(netdev, actions, clone_actions, +- clone_actions_len)) { ++ clone_actions_len, pmd)) { + return -1; ++ } else { ++ have_hard_output = true; + } + } else { + VLOG_DBG_RL(&rl, "Unsupported action type %d", nl_attr_type(nla)); +@@ -1398,6 +1572,9 @@ parse_flow_actions(struct netdev *netdev, + return -1; + } + ++#ifdef HAVE_HWOFF_AGENT ++ have_hard_output = have_hard_output; ++#endif + add_flow_action(actions, RTE_FLOW_ACTION_TYPE_END, NULL); + return 0; + } +@@ -1414,7 +1591,7 @@ netdev_offload_dpdk_actions(struct netdev *netdev, + struct rte_flow_error error; + int ret; + +- ret = parse_flow_actions(netdev, &actions, nl_actions, actions_len); ++ ret = parse_flow_actions(netdev, &actions, nl_actions, actions_len, NULL); + if (ret) { + goto out; + } +@@ -1609,3 +1786,493 @@ const struct netdev_flow_api netdev_offload_dpdk = { + .init_flow_api = netdev_offload_dpdk_init_flow_api, + .flow_get = netdev_offload_dpdk_flow_get, + }; ++ ++#ifdef HAVE_HWOFF_AGENT ++#define HIOVS_RTE_FLOW_BATCH_SIZE 16 ++static struct cmap hiovs_ufid_rte_flow_map = CMAP_INITIALIZER; ++static struct ovs_mutex hiovs_map_lock = OVS_MUTEX_INITIALIZER; ++ ++static void hiovs_rte_flow_map_lock(void) ++{ ++ ovs_mutex_lock(&hiovs_map_lock); ++} ++ ++static void hiovs_rte_flow_map_unlock(void) ++{ ++ ovs_mutex_unlock(&hiovs_map_lock); ++} ++ ++static void free_no_copy_flow_patterns(struct flow_patterns *patterns) ++{ ++ free(patterns->items); ++ patterns->items = NULL; ++ patterns->cnt = 0; ++} ++ ++static void hiovs_rte_flow_data_dealloc(struct ufid_to_rte_flow_data *flow_data) ++{ ++ hwoff_func* funcs = hwoff_get_funcs(); ++ ++ if (funcs->hwoff_rte_flow_dealloc == NULL) { ++ return; ++ } ++ funcs->hwoff_rte_flow_dealloc(flow_data->rte_flow); ++ free(flow_data); ++} ++ ++static struct ufid_to_rte_flow_data* hiovs_rte_flow_data_alloc(const ovs_u128 *ufid) ++{ ++ hwoff_func* funcs = hwoff_get_funcs(); ++ struct rte_flow *flow = NULL; ++ struct ufid_to_rte_flow_data *flow_data = NULL; ++ ++ flow_data = (struct ufid_to_rte_flow_data *)malloc(sizeof(*flow_data)); ++ if (flow_data == NULL) { ++ return NULL; ++ } ++ ++ (void)memset(flow_data, 0, sizeof(struct ufid_to_rte_flow_data)); ++ if (funcs->hwoff_rte_flow_alloc == NULL) { ++ free(flow_data); ++ return NULL; ++ } ++ ++ flow = funcs->hwoff_rte_flow_alloc((ovs_u128 *)ufid, flow_data); ++ if (flow == NULL) { ++ free(flow_data); ++ return NULL; ++ } ++ ++ flow_data->rte_flow = flow; ++ flow_data->ufid = *ufid; ++ flow_data->actions_offloaded = false; ++ flow_data->ref_cnt = 1; ++ return flow_data; ++} ++ ++static struct ufid_to_rte_flow_data* hiovs_rte_flow_data_add(const ovs_u128 *ufid) ++{ ++ size_t hash; ++ struct ufid_to_rte_flow_data *flow_data = NULL; ++ flow_data = hiovs_rte_flow_data_alloc(ufid); ++ if (flow_data == NULL) { ++ VLOG_ERR("hiovs_rte_flow_data_alloc fail, ufid="UUID_FMT, UUID_ARGS((struct uuid *)ufid)); ++ return NULL; ++ } ++ hash = hash_bytes(&flow_data->ufid, sizeof(ovs_u128), 0); ++ cmap_insert(&hiovs_ufid_rte_flow_map, &flow_data->node, hash); ++ return flow_data; ++} ++static struct ufid_to_rte_flow_data* hiovs_rte_flow_data_find(const ovs_u128 *ufid) ++{ ++ struct ufid_to_rte_flow_data *flow_data = NULL; ++ size_t hash = hash_bytes(ufid, sizeof *ufid, 0); ++ ++ CMAP_FOR_EACH_WITH_HASH (flow_data, node, hash, &hiovs_ufid_rte_flow_map) { ++ if (ovs_u128_equals(*ufid, flow_data->ufid)) { ++ return flow_data; ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct ufid_to_rte_flow_data* hiovs_rte_flow_data_get(const ovs_u128 *ufid) ++{ ++ struct ufid_to_rte_flow_data *flow_data = NULL; ++ ++ hiovs_rte_flow_map_lock(); ++ flow_data = hiovs_rte_flow_data_find(ufid); ++ if (flow_data == NULL) { ++ hiovs_rte_flow_map_unlock(); ++ ++ return NULL; ++ } ++ ++ flow_data->ref_cnt++; ++ hiovs_rte_flow_map_unlock(); ++ return flow_data; ++} ++ ++static void hiovs_rte_flow_data_close(struct ufid_to_rte_flow_data *flow_data) ++{ ++ hiovs_rte_flow_map_lock(); ++ flow_data->ref_cnt--; ++ if (flow_data->ref_cnt <= 0) { ++ hiovs_rte_flow_data_dealloc(flow_data); ++ } ++ hiovs_rte_flow_map_unlock(); ++} ++ ++uint32_t hiovs_rte_flow_list_get(ovs_u128 ufid_list[], struct rte_flow *flow_list[], uint32_t ufid_cnt) ++{ ++ int i; ++ ovs_u128 *one_ufid = NULL; ++ struct ufid_to_rte_flow_data *flow_data_list[HIOVS_RTE_FLOW_BATCH_SIZE]; ++ struct ufid_to_rte_flow_data *flow_data = NULL; ++ ++ if (ufid_cnt > HIOVS_RTE_FLOW_BATCH_SIZE) { ++ return -1; ++ } ++ ++ hiovs_rte_flow_map_lock(); ++ for (i = 0; i < ufid_cnt; i++) { ++ one_ufid = &ufid_list[i]; ++ flow_data = hiovs_rte_flow_data_find(one_ufid); ++ if (!flow_data) { ++ break; ++ } ++ ++ flow_data_list[i] = flow_data; ++ flow_list[i] = flow_data->rte_flow; ++ } ++ ++ if (i != ufid_cnt) { ++ hiovs_rte_flow_map_unlock(); ++ return -1; ++ } ++ ++ for (i = 0; i < ufid_cnt; i++) { ++ flow_data_list[i]->ref_cnt++; ++ } ++ ++ hiovs_rte_flow_map_unlock(); ++ return 0; ++} ++static struct ufid_to_rte_flow_data* hiovs_rte_flow_data_process(const ovs_u128 *ufid, void *flow) ++{ ++ bool is_dead = false; ++ struct ufid_to_rte_flow_data *flow_data = NULL; ++ hiovs_rte_flow_map_lock(); ++ is_dead = dp_netdev_flow_dead_status_get(flow); ++ if (is_dead == true) { ++ hiovs_rte_flow_map_unlock(); ++ return NULL; ++ } ++ flow_data = hiovs_rte_flow_data_find(ufid); ++ if (flow_data != NULL) { ++ flow_data->ref_cnt++; ++ hiovs_rte_flow_map_unlock(); ++ return flow_data; ++ } ++ flow_data = hiovs_rte_flow_data_add(ufid); ++ if (flow_data == NULL) { ++ hiovs_rte_flow_map_unlock(); ++ return NULL; ++ } ++ flow_data->ref_cnt++; ++ hiovs_rte_flow_map_unlock(); ++ return flow_data; ++} ++ ++void hiovs_rte_flow_data_list_put(void *flow_data_list[], uint32_t count) ++{ ++ int i; ++ struct ufid_to_rte_flow_data *flow_data = NULL; ++ ++ hiovs_rte_flow_map_lock(); ++ for (i = 0; i < count; i++) { ++ flow_data = (struct ufid_to_rte_flow_data *)(flow_data_list[i]); ++ flow_data->ref_cnt--; ++ if (flow_data->ref_cnt <= 0) { ++ hiovs_rte_flow_data_dealloc(flow_data); ++ } ++ } ++ hiovs_rte_flow_map_unlock(); ++} ++ ++static void hiovs_offload_info_parse(struct flow_patterns *patterns, ++ struct offload_info *info, ++ const ovs_u128 *sw_ufid, ++ struct rte_flow *flow) ++{ ++ add_flow_pattern(patterns, RTE_FLOW_ITEM_TYPE_PMD_ID, &info->pmd_core_id, NULL); ++ add_flow_pattern(patterns, RTE_FLOW_ITEM_TYPE_PORT_ID, &info->in_port_id, NULL); ++ add_flow_pattern(patterns, RTE_FLOW_ITEM_TYPE_PORT_TYPE, &info->in_port_type, NULL); ++ add_flow_pattern(patterns, RTE_FLOW_ITEM_TYPE_PACKETS, info->pkts_info, NULL); ++ add_flow_pattern(patterns, RTE_FLOW_ITEM_TYPE_SW_UFID, sw_ufid, NULL); ++ add_flow_pattern(patterns, RTE_FLOW_ITEM_TYPE_FLOW, flow, NULL); ++ return; ++} ++ ++static int hiovs_offload_flow_get_exec(struct netdev *netdev, struct rte_flow *rte_flow, ++ struct rte_flow_query_count *query, struct rte_flow_error *error) ++{ ++ int ret; ++ bool flag = false; ++ hwoff_func* funcs = hwoff_get_funcs(); ++ ++ flag = funcs->hwoff_is_ethdev(netdev); ++ if (flag == true) { ++ ret = netdev_dpdk_rte_flow_query_count(netdev, rte_flow, query, error); ++ return ret; ++ } ++ ++ if (funcs->hwoff_rte_flow_query_count == NULL) { ++ return -1; ++ } ++ ret = funcs->hwoff_rte_flow_query_count(netdev, rte_flow, query, error); ++ return ret; ++} ++ ++static int hiovs_offload_flow_del_exec(struct netdev *netdev, struct rte_flow *rte_flow, struct rte_flow_error *error) ++{ ++ int ret; ++ bool flag = false; ++ hwoff_func* funcs = hwoff_get_funcs(); ++ ++ if (funcs->hwoff_rte_flow_destroy == NULL) { ++ return -1; ++ } ++ ++ if (netdev == NULL) { ++ ret = funcs->hwoff_rte_flow_destroy(netdev, rte_flow, error); ++ return ret; ++ } ++ ++ flag = funcs->hwoff_is_ethdev(netdev); ++ if (flag == true) { ++ ret = netdev_dpdk_rte_flow_destroy(netdev, rte_flow, error); ++ return ret; ++ } ++ ++ ret = funcs->hwoff_rte_flow_destroy(netdev, rte_flow, error); ++ return ret; ++} ++ ++static struct rte_flow* hiovs_offload_flow_add_exec(struct netdev *netdev, ++ const struct rte_flow_attr *attr, ++ const struct rte_flow_item *items, ++ const struct rte_flow_action *actions, ++ struct rte_flow_error *error) ++{ ++ bool flag = false; ++ struct rte_flow *flow = NULL; ++ hwoff_func* funcs = hwoff_get_funcs(); ++ ++ flag = funcs->hwoff_is_ethdev(netdev); ++ if (flag == true) { ++ flow = netdev_dpdk_rte_flow_create(netdev, attr, items, actions, error); ++ return flow; ++ } ++ ++ if (funcs->hwoff_rte_flow_create == NULL) { ++ return NULL; ++ } ++ flow = funcs->hwoff_rte_flow_create(netdev, attr, items, actions, error); ++ return flow; ++} ++ ++static int hiovs_offload_flow_add(struct netdev *netdev, ++ struct ufid_to_rte_flow_data *flow_data, ++ struct nlattr *nl_actions, ++ size_t actions_len, ++ struct offload_info *info) ++{ ++ int ret; ++ struct rte_flow *flow = NULL; ++ struct rte_flow_error error; ++ const struct rte_flow_attr flow_attr = { .ingress = 1, .transfer = 1 }; ++ struct flow_actions actions = { .actions = NULL, .cnt = 0 }; ++ struct flow_patterns patterns = { .items = NULL, .cnt = 0 }; ++ ++ hiovs_offload_info_parse(&patterns, info, &flow_data->ufid, flow_data->rte_flow); ++ ret = parse_flow_actions(netdev, &actions, nl_actions, actions_len, info->pmd); ++ if (ret != 0) { ++ goto out; ++ } ++ ++ memset(&error, 0, sizeof(error)); ++ flow = hiovs_offload_flow_add_exec(netdev, &flow_attr, patterns.items, actions.actions, &error); ++ if (flow == NULL) { ++ ret = -1; ++ goto out; ++ } ++ ++ flow_data->actions_offloaded = true; ++ ret = 0; ++out: ++ free_no_copy_flow_patterns(&patterns); ++ free_flow_actions(&actions); ++ return ret; ++} ++ ++int hiovs_offload_flow_api_del(struct netdev *netdev, const ovs_u128 *ufid, struct dpif_flow_stats *stats) ++{ ++ int ret; ++ hwoff_func* funcs = hwoff_get_funcs(); ++ struct rte_flow_error error; ++ struct ufid_to_rte_flow_data *flow_data; ++ size_t hash; ++ ++ hiovs_rte_flow_map_lock(); ++ flow_data = hiovs_rte_flow_data_find(ufid); ++ if (flow_data == NULL) { ++ hiovs_rte_flow_map_unlock(); ++ return 0; ++ } ++ hiovs_rte_flow_map_unlock(); ++ ++ funcs->hwoff_rte_flow_deleted_set(flow_data->rte_flow, true); ++ ret = hiovs_offload_flow_del_exec(netdev, flow_data->rte_flow, &error); ++ ++ hash = hash_bytes(&flow_data->ufid, sizeof(ovs_u128), 0); ++ hiovs_rte_flow_map_lock(); ++ cmap_remove(&hiovs_ufid_rte_flow_map, &flow_data->node, hash); ++ flow_data->ref_cnt--; ++ if (flow_data->ref_cnt <= 0) { ++ hiovs_rte_flow_data_dealloc(flow_data); ++ } ++ hiovs_rte_flow_map_unlock(); ++ ++ if (stats) { ++ memset(stats, 0, sizeof *stats); ++ } ++ return ret; ++} ++ ++static int hiovs_offload_flow_api_put(struct netdev *netdev, struct match *match OVS_UNUSED, ++ struct nlattr *actions, size_t actions_len, ++ const ovs_u128 *ufid, struct offload_info *info, ++ struct dpif_flow_stats *stats) ++{ ++ int ret; ++ bool is_delete = false; ++ hwoff_func* funcs = hwoff_get_funcs(); ++ struct ufid_to_rte_flow_data *flow_data = NULL; ++ ++ /* When modification is true, we just destroy rte_flow. */ ++ if (info->modification) { ++ ret = hiovs_offload_flow_api_del(netdev, ufid, stats); ++ return ret; ++ } ++ flow_data = hiovs_rte_flow_data_process(ufid, info->flow); ++ if (flow_data == NULL) { ++ return -1; ++ } ++ ++ is_delete = funcs->hwoff_rte_flow_deleted_get(flow_data->rte_flow); ++ if (is_delete) { ++ hiovs_rte_flow_data_close(flow_data); ++ return -1; ++ } ++ ++ ret = hiovs_offload_flow_add(netdev, flow_data, actions, actions_len, info); ++ if (ret != 0) { ++ hiovs_rte_flow_data_close(flow_data); ++ return ret; ++ } ++ ++ if (stats) { ++ *stats = flow_data->stats; ++ } ++ hiovs_rte_flow_data_close(flow_data); ++ return 0; ++} ++ ++static int hiovs_offload_flow_api_get(struct netdev *netdev, ++ struct match *match OVS_UNUSED, ++ struct nlattr **actions OVS_UNUSED, ++ const ovs_u128 *ufid, ++ struct dpif_flow_stats *stats, ++ struct dpif_flow_attrs *attrs, ++ struct ofpbuf *buf OVS_UNUSED) ++{ ++ int ret = 0; ++ struct rte_flow_query_count query = { .reset = 1 }; ++ struct ufid_to_rte_flow_data *flow_data = NULL; ++ struct rte_flow_error error; ++ ++ flow_data = hiovs_rte_flow_data_get(ufid); ++ if (flow_data == NULL) { ++ attrs->dp_extra_info = NULL; ++ return -1; ++ } ++ ++ attrs->offloaded = true; ++ if (flow_data->actions_offloaded == false) { ++ attrs->dp_layer = "ovs"; ++ memset(stats, 0, sizeof *stats); ++ goto out; ++ } ++ ++ attrs->dp_layer = "dpdk"; ++ ret = hiovs_offload_flow_get_exec(netdev, flow_data->rte_flow, &query, &error); ++ if (ret) { ++ VLOG_DBG_RL(&rl, "%s: Failed to query ufid "UUID_FMT" flow: %p", ++ netdev_get_name(netdev), UUID_ARGS((struct uuid *) ufid), flow_data->rte_flow); ++ goto out; ++ } ++ ++ flow_data->stats.n_packets += query.hits; ++ flow_data->stats.n_bytes += query.bytes; ++ if (query.hits_set && query.hits) { ++ flow_data->stats.used = time_msec(); ++ } ++ memcpy(stats, &flow_data->stats, sizeof *stats); ++out: ++ hiovs_rte_flow_data_close(flow_data); ++ attrs->dp_extra_info = NULL; ++ return ret; ++} ++ ++static int hiovs_offload_flow_api_init(struct netdev *netdev) ++{ ++ bool flag = false; ++ hwoff_func* funcs = hwoff_get_funcs(); ++ ++ if (strcmp(netdev->netdev_class->type, "vxlan") == 0) { ++ return 0; ++ } ++ ++ if (strcmp(netdev->netdev_class->type, "tap") == 0) { ++ return 0; ++ } ++ ++ flag = funcs->hwoff_is_ethdev(netdev); ++ if (flag == true) { ++ return 0; ++ } ++ ++ return -1; ++} ++ ++static void hiovs_offload_dump_rte_flows(struct unixctl_conn *conn, int argc OVS_UNUSED, ++ const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) ++{ ++ int count = 0; ++ hwoff_func* funcs = hwoff_get_funcs(); ++ struct ds ds = DS_EMPTY_INITIALIZER; ++ struct cmap_cursor cursor; ++ struct ufid_to_rte_flow_data *flow_data = NULL; ++ ++ hiovs_rte_flow_map_lock(); ++ CMAP_CURSOR_FOR_EACH(flow_data, node, &cursor, &hiovs_ufid_rte_flow_map) { ++ odp_format_ufid(&flow_data->ufid, &ds); ++ ds_put_format(&ds, ", is_deleted=%d, ref_cnt=%d\n", ++ funcs->hwoff_rte_flow_deleted_get(flow_data->rte_flow), flow_data->ref_cnt); ++ count++; ++ } ++ hiovs_rte_flow_map_unlock(); ++ ds_put_format(&ds, "rte_flow_count=%d\n", count); ++ unixctl_command_reply(conn, ds_cstr(&ds)); ++ ds_destroy(&ds); ++ return; ++} ++ ++int hiovs_netdev_offload_init(void) ++{ ++ ovs_mutex_init(&hiovs_map_lock); ++ unixctl_command_register("hwoff/dump-rte-flows", "", 0, 0, hiovs_offload_dump_rte_flows, NULL); ++ return 0; ++} ++ ++const struct netdev_flow_api hiovs_netdev_offload_api = { ++ .type = "hiovs_netdev_offload_api", ++ .flow_put = hiovs_offload_flow_api_put, ++ .flow_del = hiovs_offload_flow_api_del, ++ .init_flow_api = hiovs_offload_flow_api_init, ++ .flow_get = hiovs_offload_flow_api_get, ++}; ++#endif ++ +diff --git a/openvswitch-2.14.2/lib/netdev-offload-provider.h b/openvswitch-2.14.2/lib/netdev-offload-provider.h +index 0bed7bf..83f393d 100644 +--- a/openvswitch-2.14.2/lib/netdev-offload-provider.h ++++ b/openvswitch-2.14.2/lib/netdev-offload-provider.h +@@ -100,6 +100,13 @@ extern const struct netdev_flow_api netdev_offload_tc; + extern const struct netdev_flow_api netdev_offload_dpdk; + #endif + ++#ifdef HAVE_HWOFF_AGENT ++extern const struct netdev_flow_api hiovs_netdev_offload_api; ++int hiovs_netdev_offload_init(void); ++uint32_t hiovs_rte_flow_list_get(ovs_u128 *ufid, struct rte_flow **flow_list, uint32_t ufid_cnt); ++void hiovs_rte_flow_data_list_put(void *flow_data_list[], uint32_t count); ++#endif ++ + #ifdef __cplusplus + } + #endif +diff --git a/openvswitch-2.14.2/lib/netdev-offload.c b/openvswitch-2.14.2/lib/netdev-offload.c +index 2da3bc7..cefb4b7 100644 +--- a/openvswitch-2.14.2/lib/netdev-offload.c ++++ b/openvswitch-2.14.2/lib/netdev-offload.c +@@ -26,10 +26,15 @@ + #include + #include + ++#ifdef HAVE_HWOFF_AGENT ++#include "dp-packet.h" ++#include "ovs-numa.h" ++#include "hwoff_init_func.h" ++#endif ++ + #include "cmap.h" + #include "coverage.h" + #include "dpif.h" +-#include "dp-packet.h" + #include "openvswitch/dynamic-string.h" + #include "fatal-signal.h" + #include "hash.h" +@@ -174,6 +179,12 @@ netdev_assign_flow_api(struct netdev *netdev) + struct netdev_registered_flow_api *rfa; + + CMAP_FOR_EACH (rfa, cmap_node, &netdev_flow_apis) { ++ if (strcmp(rfa->flow_api->type, "linux_tc") == 0) { ++ if (strcmp(netdev->netdev_class->type, "tap") == 0) { ++ continue; ++ } ++ } ++ + if (!rfa->flow_api->init_flow_api(netdev)) { + ovs_refcount_ref(&rfa->refcnt); + ovsrcu_set(&netdev->flow_api, rfa->flow_api); +@@ -531,6 +542,40 @@ netdev_ports_lookup(odp_port_t port_no, const char *dpif_type) + return NULL; + } + ++#ifdef HAVE_HWOFF_AGENT ++int ++netdev_ports_insert(struct netdev *netdev, const char *dpif_type, ++ struct dpif_port *dpif_port) ++{ ++ struct port_to_netdev_data *data; ++ int ifindex = netdev_get_ifindex(netdev); ++ ++ ovs_rwlock_wrlock(&netdev_hmap_rwlock); ++ if (netdev_ports_lookup(dpif_port->port_no, dpif_type)) { ++ ovs_rwlock_unlock(&netdev_hmap_rwlock); ++ return EEXIST; ++ } ++ ++ data = xzalloc(sizeof *data); ++ data->netdev = netdev_ref(netdev); ++ dpif_port_clone(&data->dpif_port, dpif_port); ++ data->ifindex = ifindex; ++ ++ netdev_set_dpif_type(netdev, dpif_type); ++ ++ hmap_insert(&port_to_netdev, &data->portno_node, ++ netdev_ports_hash(dpif_port->port_no, dpif_type)); ++ if (ifindex >= 0) { ++ hmap_insert(&ifindex_to_port, &data->ifindex_node, ifindex); ++ } ++ ovs_rwlock_unlock(&netdev_hmap_rwlock); ++ ++ netdev_init_flow_api(netdev); ++ ++ return 0; ++} ++ ++#else + int + netdev_ports_insert(struct netdev *netdev, const char *dpif_type, + struct dpif_port *dpif_port) +@@ -564,6 +609,7 @@ netdev_ports_insert(struct netdev *netdev, const char *dpif_type, + + return 0; + } ++#endif + + struct netdev * + netdev_ports_get(odp_port_t port_no, const char *dpif_type) +@@ -593,7 +639,13 @@ netdev_ports_remove(odp_port_t port_no, const char *dpif_type) + dpif_port_destroy(&data->dpif_port); + netdev_close(data->netdev); /* unref and possibly close */ + hmap_remove(&port_to_netdev, &data->portno_node); ++#ifdef HAVE_HWOFF_AGENT ++ if (data->ifindex >= 0) { ++ hmap_remove(&ifindex_to_port, &data->ifindex_node); ++ } ++#else + hmap_remove(&ifindex_to_port, &data->ifindex_node); ++#endif + free(data); + ret = 0; + } +@@ -666,3 +718,40 @@ netdev_set_flow_api_enabled(const struct smap *ovs_other_config) + } + } + } ++ ++#ifdef HAVE_HWOFF_AGENT ++static void ++hiovs_rte_pktmbuf_init(struct rte_mempool *mp OVS_UNUSED, ++ void *opaque_arg OVS_UNUSED, ++ void *_p, ++ unsigned i OVS_UNUSED) ++{ ++ struct rte_mbuf *pkt = _p; ++ ++ dp_packet_init_dpdk((struct dp_packet *) pkt); ++} ++ ++int ++netdev_offload_hw_init(const struct smap *ovs_other_config) ++{ ++ int ret; ++ static bool hwoff_agent_init = false; ++ ++ if (OVS_LIKELY(hwoff_agent_init)) { ++ return 0; ++ } ++ ++ if (smap_get_bool(ovs_other_config, "hw-offload", false)) { ++ ret = hwoff_funcs_init(); ++ if (ret != 0) { ++ return ret; ++ } ++ (void)hiovs_netdev_offload_init(); ++ netdev_register_flow_api_provider(&hiovs_netdev_offload_api); ++ hwoff_agent_init = true; ++ return ret; ++ } ++ ++ return 0; ++} ++#endif +diff --git a/openvswitch-2.14.2/lib/netdev-offload.h b/openvswitch-2.14.2/lib/netdev-offload.h +index 4c0ed2a..f91c6d0 100644 +--- a/openvswitch-2.14.2/lib/netdev-offload.h ++++ b/openvswitch-2.14.2/lib/netdev-offload.h +@@ -22,13 +22,18 @@ + #include "openvswitch/types.h" + #include "packets.h" + #include "flow.h" ++#ifdef HAVE_HWOFF_AGENT ++#include "dp-packet.h" ++#endif + + #ifdef __cplusplus + extern "C" { + #endif + ++#ifndef HAVE_HWOFF_AGENT + struct dp_packet_batch; + struct dp_packet; ++#endif + struct netdev_class; + struct netdev_rxq; + struct netdev_saved_flags; +@@ -73,7 +78,15 @@ struct offload_info { + * it will be in the pkt meta data. + */ + uint32_t flow_mark; +- ++#ifdef HAVE_HWOFF_AGENT ++ uint32_t in_port_id; ++ uint32_t in_port_type; ++ unsigned int pmd_core_id; ++ void *pmd; ++ void *flow; ++ struct dp_packet_batch *pkts_info; ++ bool modification; ++#endif + bool tc_modify_flow_deleted; /* Indicate the tc modify flow put success + * to delete the original flow. */ + }; +@@ -125,6 +138,11 @@ int netdev_ports_flow_get(const char *dpif_type, struct match *match, + struct dpif_flow_attrs *attrs, + struct ofpbuf *buf); + ++#ifdef HAVE_HWOFF_AGENT ++int netdev_offload_hw_init(const struct smap *ovs_other_config); ++int hiovs_offload_flow_api_del(struct netdev *netdev, const ovs_u128 *ufid, struct dpif_flow_stats *stats); ++#endif ++ + #ifdef __cplusplus + } + #endif +diff --git a/openvswitch-2.14.2/lib/netdev-vport.c b/openvswitch-2.14.2/lib/netdev-vport.c +index 7c99f79..48c8399 100644 +--- a/openvswitch-2.14.2/lib/netdev-vport.c ++++ b/openvswitch-2.14.2/lib/netdev-vport.c +@@ -50,6 +50,10 @@ + #ifdef __linux__ + #include "netdev-linux.h" + #endif ++#ifdef HAVE_HWOFF_AGENT ++#include ++#include "hwoff_init_func.h" ++#endif + + VLOG_DEFINE_THIS_MODULE(netdev_vport); + +@@ -458,6 +462,20 @@ vxlan_get_port_ext_gbp_str(uint16_t port, bool gbp, + return namebuf; + } + ++#ifdef HAVE_HWOFF_AGENT ++static bool ++hwoff_is_ipv6_addr(struct in6_addr *ip6) ++{ ++ if (ip6->__in6_u.__u6_addr32[0] == 0 && ++ ip6->__in6_u.__u6_addr32[1] == 0 && ++ ip6->__in6_u.__u6_addr32[2] == 0xffff0000 && ++ ip6->__in6_u.__u6_addr32[3] != 0) { ++ return false; ++ } ++ return true; ++} ++#endif ++ + static void + update_vxlan_global_cfg(struct netdev *netdev, + struct netdev_tunnel_config *old_cfg, +@@ -468,6 +486,17 @@ update_vxlan_global_cfg(struct netdev *netdev, + const char *type = netdev_get_type(netdev); + struct vport_class *vclass = vport_class_cast(netdev_get_class(netdev)); + ++#ifdef HAVE_HWOFF_AGENT ++ hwoff_func* funcs = hwoff_get_funcs(); ++ bool is_ipv6 = false; ++ if (strcmp(type, "vxlan") == 0 && new_cfg && ++ funcs->hwoff_global_add_vxlan_vtep && ++ (old_cfg == NULL || memcmp(&new_cfg->ipv6_src, &old_cfg->ipv6_src, sizeof(struct in6_addr)))) { ++ is_ipv6 = hwoff_is_ipv6_addr(&new_cfg->ipv6_src); ++ funcs->hwoff_global_add_vxlan_vtep(is_ipv6, &new_cfg->ipv6_src, sizeof(struct in6_addr), new_cfg->dst_port); ++ } ++#endif ++ + if (strcmp(type, "vxlan") || + (old_cfg != NULL && new_cfg != NULL && + old_cfg->dst_port == new_cfg->dst_port && +@@ -487,6 +516,12 @@ update_vxlan_global_cfg(struct netdev *netdev, + simap_put(&vclass->global_cfg_tracker, namebuf, count); + } else { + simap_find_and_delete(&vclass->global_cfg_tracker, namebuf); ++#ifdef HAVE_HWOFF_AGENT ++ if (funcs->hwoff_global_del_vxlan_vtep) { ++ is_ipv6 = hwoff_is_ipv6_addr(&old_cfg->ipv6_src); ++ funcs->hwoff_global_del_vxlan_vtep(is_ipv6, &old_cfg->ipv6_src, sizeof(struct in6_addr)); ++ } ++#endif + } + } + } +diff --git a/openvswitch-2.14.2/lib/odp-util.c b/openvswitch-2.14.2/lib/odp-util.c +index a8598d5..7ca937a 100644 +--- a/openvswitch-2.14.2/lib/odp-util.c ++++ b/openvswitch-2.14.2/lib/odp-util.c +@@ -4511,6 +4511,12 @@ odp_format_ufid(const ovs_u128 *ufid, struct ds *ds) + ds_put_format(ds, "ufid:"UUID_FMT, UUID_ARGS((struct uuid *)ufid)); + } + ++void ++odp_format_mega_ufid(const ovs_u128 *ufid, struct ds *ds) ++{ ++ ds_put_format(ds, "mega_ufid:"UUID_FMT, UUID_ARGS((struct uuid *)ufid)); ++} ++ + /* Appends to 'ds' a string representation of the 'key_len' bytes of + * OVS_KEY_ATTR_* attributes in 'key'. If non-null, additionally formats the + * 'mask_len' bytes of 'mask' which apply to 'key'. If 'portno_names' is +diff --git a/openvswitch-2.14.2/lib/odp-util.h b/openvswitch-2.14.2/lib/odp-util.h +index a1d0d0f..ca2e3e6 100644 +--- a/openvswitch-2.14.2/lib/odp-util.h ++++ b/openvswitch-2.14.2/lib/odp-util.h +@@ -166,8 +166,8 @@ enum odp_key_fitness odp_nsh_hdr_from_attr(const struct nlattr *, + struct nsh_hdr *, size_t); + + int odp_ufid_from_string(const char *s_, ovs_u128 *ufid); +-void odp_format_ufid(const ovs_u128 *ufid, struct ds *); +- ++void odp_format_ufid(const ovs_u128 *ufid, struct ds *); ++void odp_format_mega_ufid(const ovs_u128 *ufid, struct ds *); + void odp_flow_format(const struct nlattr *key, size_t key_len, + const struct nlattr *mask, size_t mask_len, + const struct hmap *portno_names, struct ds *, +diff --git a/openvswitch-2.14.2/lib/packets.h b/openvswitch-2.14.2/lib/packets.h +index 395bc86..02c3932 100644 +--- a/openvswitch-2.14.2/lib/packets.h ++++ b/openvswitch-2.14.2/lib/packets.h +@@ -1537,6 +1537,8 @@ BUILD_ASSERT_DECL(sizeof(struct vxlanhdr) == 8); + + #define VXLAN_FLAGS 0x08000000 /* struct vxlanhdr.vx_flags required value. */ + ++#define IP_MAX_MASK_LEN 32 ++#define IPV6_MAX_MASK_LEN 128 + /* + * VXLAN Generic Protocol Extension (VXLAN_F_GPE): + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +diff --git a/openvswitch-2.14.2/lib/smap.c b/openvswitch-2.14.2/lib/smap.c +index 149b8b2..e4f4527 100644 +--- a/openvswitch-2.14.2/lib/smap.c ++++ b/openvswitch-2.14.2/lib/smap.c +@@ -393,6 +393,17 @@ smap_add__(struct smap *smap, char *key, void *value, size_t hash) + return node; + } + ++void ++smap_add_format_varg(struct smap *smap, const char *key, const char *format, va_list args) ++{ ++ char *value; ++ size_t key_len; ++ ++ value = xvasprintf(format, args); ++ key_len = strlen(key); ++ smap_add__(smap, xmemdup0(key, key_len), value, hash_bytes(key, key_len, 0)); ++} ++ + static struct smap_node * + smap_find__(const struct smap *smap, const char *key, size_t key_len, + size_t hash) +diff --git a/openvswitch-2.14.2/lib/smap.h b/openvswitch-2.14.2/lib/smap.h +index 766c65f..3d9d1c6 100644 +--- a/openvswitch-2.14.2/lib/smap.h ++++ b/openvswitch-2.14.2/lib/smap.h +@@ -89,6 +89,7 @@ struct smap_node *smap_add_nocopy(struct smap *, char *, char *); + bool smap_add_once(struct smap *, const char *, const char *); + void smap_add_format(struct smap *, const char *key, const char *, ...) + OVS_PRINTF_FORMAT(3, 4); ++void smap_add_format_varg(struct smap *smap, const char *key, const char *format, va_list args); + void smap_add_ipv6(struct smap *, const char *, struct in6_addr *); + void smap_replace(struct smap *, const char *, const char *); + void smap_replace_nocopy(struct smap *, const char *, char *); +diff --git a/openvswitch-2.14.2/ofproto/ofproto-dpif-upcall.c b/openvswitch-2.14.2/ofproto/ofproto-dpif-upcall.c +index b24547d..dfec310 100644 +--- a/openvswitch-2.14.2/ofproto/ofproto-dpif-upcall.c ++++ b/openvswitch-2.14.2/ofproto/ofproto-dpif-upcall.c +@@ -409,6 +409,9 @@ static int udpif_flow_unprogram(struct udpif *udpif, struct udpif_key *ukey, + static upcall_callback upcall_cb; + static dp_purge_callback dp_purge_cb; + ++#ifdef HAVE_HWOFF_AGENT ++static dp_pmd_ukey_purge_callback dp_pmd_ukey_purge_cb; ++#endif + static atomic_bool enable_megaflows = ATOMIC_VAR_INIT(true); + static atomic_bool enable_ufid = ATOMIC_VAR_INIT(true); + +@@ -463,6 +466,9 @@ udpif_create(struct dpif_backer *backer, struct dpif *dpif) + dpif_register_upcall_cb(dpif, upcall_cb, udpif); + dpif_register_dp_purge_cb(dpif, dp_purge_cb, udpif); + ++#ifdef HAVE_HWOFF_AGENT ++ dpif_register_dp_pmd_ukey_purge_cb(dpif, dp_pmd_ukey_purge_cb, udpif); ++#endif + return udpif; + } + +@@ -489,6 +495,9 @@ udpif_destroy(struct udpif *udpif) + dpif_register_dp_purge_cb(udpif->dpif, NULL, udpif); + dpif_register_upcall_cb(udpif->dpif, NULL, udpif); + ++#ifdef HAVE_HWOFF_AGENT ++ dpif_register_dp_pmd_ukey_purge_cb(udpif->dpif, NULL, udpif); ++#endif + for (int i = 0; i < N_UMAPS; i++) { + cmap_destroy(&udpif->ukeys[i].cmap); + ovs_mutex_destroy(&udpif->ukeys[i].mutex); +@@ -965,7 +974,7 @@ udpif_revalidator(void *arg) + flow_limit < n_flows * 1000 / duration) { + flow_limit += 1000; + } +- flow_limit = MIN(ofproto_flow_limit, MAX(flow_limit, 1000)); ++ flow_limit = MIN(ofproto_flow_limit, MAX(flow_limit, 3000)); + atomic_store_relaxed(&udpif->flow_limit, flow_limit); + + if (duration > 2000) { +@@ -2874,7 +2883,24 @@ dp_purge_cb(void *aux, unsigned pmd_id) + } + udpif_resume_revalidators(udpif); + } +- ++#ifdef HAVE_HWOFF_AGENT ++static void dp_pmd_ukey_purge_cb(void *aux, unsigned pmd_id) ++{ ++ struct udpif *udpif = aux; ++ int i; ++ for (i = 0; i < N_UMAPS; i++) { ++ struct udpif_key *ukey; ++ struct umap *umap = &udpif->ukeys[i]; ++ ovs_mutex_lock(&umap->mutex); ++ CMAP_FOR_EACH(ukey, cmap_node, &umap->cmap) { ++ if (ukey->pmd_id == pmd_id) { ++ ukey_delete(umap, ukey); ++ } ++ } ++ ovs_mutex_unlock(&umap->mutex); ++ } ++} ++#endif + static void + upcall_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) +diff --git a/openvswitch-2.14.2/ofproto/ofproto-dpif-xlate.c b/openvswitch-2.14.2/ofproto/ofproto-dpif-xlate.c +index 1f78da1..1713578 100644 +--- a/openvswitch-2.14.2/ofproto/ofproto-dpif-xlate.c ++++ b/openvswitch-2.14.2/ofproto/ofproto-dpif-xlate.c +@@ -33,6 +33,7 @@ + #include "coverage.h" + #include "csum.h" + #include "dp-packet.h" ++#include "dpif-provider.h" + #include "dpif.h" + #include "in-band.h" + #include "lacp.h" +@@ -87,6 +88,10 @@ VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate); + * Outputs to patch ports and to groups also count against the depth limit. */ + #define MAX_DEPTH 64 + ++#ifdef HAVE_HWOFF_AGENT ++#define NETDEV_NAME "netdev" ++#endif ++ + /* Maximum number of resubmit actions in a flow translation, whether they are + * recursive or not. */ + #define MAX_RESUBMITS (MAX_DEPTH * MAX_DEPTH) +@@ -2590,13 +2595,56 @@ update_learning_table__(const struct xbridge *xbridge, + struct xbundle *in_xbundle, struct eth_addr dl_src, + int vlan, bool is_grat_arp) + { ++#ifdef HAVE_HWOFF_AGENT ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); ++ void *out_ofbundle = NULL; ++ bool update = (in_xbundle == &ofpp_none_bundle); ++ ++ if (update) { ++ return update; ++ } ++ update = mac_learning_update(xbridge->ml, dl_src, vlan, ++ is_grat_arp, ++ in_xbundle->bond != NULL, ++ in_xbundle->ofbundle, &out_ofbundle); ++ if (update && out_ofbundle) { ++ VLOG_INFO_RL(&rl, "mac learning conflicted. "ETH_ADDR_FMT" is on new port %s in VLAN %d, old port is %s", ++ ETH_ADDR_ARGS(dl_src), in_xbundle->name, vlan, ofbundle_get_name(out_ofbundle)); ++ } ++ ++ return !update; ++#else + return (in_xbundle == &ofpp_none_bundle + || !mac_learning_update(xbridge->ml, dl_src, vlan, + is_grat_arp, + in_xbundle->bond != NULL, + in_xbundle->ofbundle)); ++#endif + } ++#ifdef HAVE_HWOFF_AGENT ++static void ++update_learning_table(const struct xlate_ctx *ctx, ++ struct xbundle *in_xbundle, struct eth_addr dl_src, ++ int vlan, bool is_grat_arp, bool is_reverse_arp, bool is_ipv6_nd) ++{ ++ struct hwoff_migrate_rarp_mac_infos *hwoff_migrate_rarp_mac_infos = hwoff_migrate_rarp_mac_infos_get(); ++ if (!update_learning_table__(ctx->xbridge, in_xbundle, dl_src, vlan, ++ is_grat_arp)) { ++ xlate_report_debug(ctx, OFT_DETAIL, "learned that "ETH_ADDR_FMT" is " ++ "on port %s in VLAN %d", ++ ETH_ADDR_ARGS(dl_src), in_xbundle->name, vlan); + ++ if ((unlikely(is_reverse_arp) || unlikely(is_grat_arp) || unlikely(is_ipv6_nd)) && ++ hwoff_rarp_status_get() && (!strcmp(ctx->xbridge->dpif->dpif_class->type, NETDEV_NAME))) { ++ ovs_rwlock_wrlock(&hwoff_migrate_rarp_mac_infos->rw); ++ if (hwoff_rarp_mac_insert_to_list(dl_src) == NULL) { ++ xlate_report(ctx, OFT_WARN, "insert failed ! the rarp mac length exceeds the upper limit."); ++ } ++ ovs_rwlock_unlock(&hwoff_migrate_rarp_mac_infos->rw); ++ } ++ } ++} ++#else + static void + update_learning_table(const struct xlate_ctx *ctx, + struct xbundle *in_xbundle, struct eth_addr dl_src, +@@ -2609,6 +2657,7 @@ update_learning_table(const struct xlate_ctx *ctx, + ETH_ADDR_ARGS(dl_src), in_xbundle->name, vlan); + } + } ++#endif + + /* Updates multicast snooping table 'ms' given that a packet matching 'flow' + * was received on 'in_xbundle' in 'vlan' and is either Report or Query. */ +@@ -3010,15 +3059,23 @@ xlate_normal(struct xlate_ctx *ctx) + if (in_port && !is_admissible(ctx, in_port, vlan)) { + return; + } +- + /* Learn source MAC. */ + bool is_grat_arp = is_gratuitous_arp(flow, wc); ++#ifdef HAVE_HWOFF_AGENT ++ bool is_reverse_arp = (flow->dl_type == htons(ETH_TYPE_RARP)) ? true : false; ++ bool is_ipv6_nd = is_nd(flow, NULL); ++#endif + if (ctx->xin->allow_side_effects + && flow->packet_type == htonl(PT_ETH) + && in_port->pt_mode != NETDEV_PT_LEGACY_L3 + ) { +- update_learning_table(ctx, in_xbundle, flow->dl_src, vlan, ++#ifdef HAVE_HWOFF_AGENT ++ update_learning_table(ctx, in_xbundle, flow->dl_src, vlan, ++ is_grat_arp, is_reverse_arp, is_ipv6_nd); ++#else ++ update_learning_table(ctx, in_xbundle, flow->dl_src, vlan, + is_grat_arp); ++#endif + } + if (ctx->xin->xcache && in_xbundle != &ofpp_none_bundle) { + struct xc_entry *entry; +diff --git a/openvswitch-2.14.2/ofproto/ofproto-dpif.c b/openvswitch-2.14.2/ofproto/ofproto-dpif.c +index 4f0638f..c9dc9c1 100644 +--- a/openvswitch-2.14.2/ofproto/ofproto-dpif.c ++++ b/openvswitch-2.14.2/ofproto/ofproto-dpif.c +@@ -193,6 +193,11 @@ ofport_dpif_cast(const struct ofport *ofport) + return ofport ? CONTAINER_OF(ofport, struct ofport_dpif, up) : NULL; + } + ++char * ++ofbundle_get_name(const void *ofbundle_) ++{ ++ return ((struct ofbundle *)ofbundle_)->name; ++} + 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 *); +@@ -5292,6 +5297,18 @@ type_set_config(const char *type, const struct smap *other_config) + dpif_set_config(backer->dpif, other_config); + } + ++#ifdef HAVE_HWOFF_AGENT ++static void ++ct_flush(const struct ofproto *ofproto_, const uint16_t *zone, ++ union ct_addr *sip, union ct_addr *dip, ++ union ct_addr*smask, union ct_addr *dmask, ++ uint16_t dl_type, bool is_force) ++{ ++ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); ++ ++ ct_dpif_flush(ofproto->backer->dpif, zone, sip, dip, smask, dmask, dl_type, is_force); ++} ++#else + static void + ct_flush(const struct ofproto *ofproto_, const uint16_t *zone) + { +@@ -5299,6 +5316,7 @@ ct_flush(const struct ofproto *ofproto_, const uint16_t *zone) + + ct_dpif_flush(ofproto->backer->dpif, zone, NULL); + } ++#endif + + static struct ct_timeout_policy * + ct_timeout_policy_lookup(const struct hmap *ct_tps, struct simap *tp) +diff --git a/openvswitch-2.14.2/ofproto/ofproto-dpif.h b/openvswitch-2.14.2/ofproto/ofproto-dpif.h +index 1f5794f..9ae0629 100644 +--- a/openvswitch-2.14.2/ofproto/ofproto-dpif.h ++++ b/openvswitch-2.14.2/ofproto/ofproto-dpif.h +@@ -349,6 +349,7 @@ struct ofproto_dpif { + * switch connection. */ + }; + ++char *ofbundle_get_name(const void *ofbundle_); + struct ofproto_dpif *ofproto_dpif_lookup_by_name(const char *name); + struct ofproto_dpif *ofproto_dpif_lookup_by_uuid(const struct uuid *uuid); + +diff --git a/openvswitch-2.14.2/ofproto/ofproto-provider.h b/openvswitch-2.14.2/ofproto/ofproto-provider.h +index afecb24..ff46587 100644 +--- a/openvswitch-2.14.2/ofproto/ofproto-provider.h ++++ b/openvswitch-2.14.2/ofproto/ofproto-provider.h +@@ -58,6 +58,7 @@ + #include "tun-metadata.h" + #include "versions.h" + #include "vl-mff-map.h" ++#include "conntrack.h" + + struct match; + struct ofputil_flow_mod; +@@ -1895,7 +1896,14 @@ struct ofproto_class { + /* ## ------------------- ## */ + /* Flushes the connection tracking tables. If 'zone' is not NULL, + * only deletes connections in '*zone'. */ ++#ifdef HAVE_HWOFF_AGENT ++ void (*ct_flush)(const struct ofproto *, const uint16_t *zone, ++ union ct_addr *sip, union ct_addr *dip, ++ union ct_addr *smask, union ct_addr *dmask, ++ uint16_t dl_type, bool is_force); ++#else + void (*ct_flush)(const struct ofproto *, const uint16_t *zone); ++#endif + + /* Sets conntrack timeout policy specified by 'timeout_policy' to 'zone' + * in datapath type 'dp_type'. */ +diff --git a/openvswitch-2.14.2/ofproto/ofproto.c b/openvswitch-2.14.2/ofproto/ofproto.c +index 59f06aa..d23ad15 100644 +--- a/openvswitch-2.14.2/ofproto/ofproto.c ++++ b/openvswitch-2.14.2/ofproto/ofproto.c +@@ -941,7 +941,11 @@ handle_nxt_ct_flush_zone(struct ofconn *ofconn, const struct ofp_header *oh) + + uint16_t zone = ntohs(nzi->zone_id); + if (ofproto->ofproto_class->ct_flush) { ++#ifdef HAVE_HWOFF_AGENT ++ ofproto->ofproto_class->ct_flush(ofproto, &zone, NULL, NULL, NULL, NULL, 0, false); ++#else + ofproto->ofproto_class->ct_flush(ofproto, &zone); ++#endif + } else { + return EOPNOTSUPP; + } +diff --git a/openvswitch-2.14.2/tests/hiovs-offload.at b/openvswitch-2.14.2/tests/hiovs-offload.at +new file mode 100644 +index 0000000..41d9ea5 +--- /dev/null ++++ b/openvswitch-2.14.2/tests/hiovs-offload.at +@@ -0,0 +1,10 @@ ++AT_BANNER([hiovs offload unit tests]) ++m4_foreach( ++ [testname], ++ [[init_flow_api], ++ [flow_put], ++ [flow_del], ++ [flow_get]], ++ [AT_SETUP([hiovs offload- m4_bpatsubst(testname, [-], [ ])]) ++ AT_CHECK([ovstest test-hiovs-offload m4_bpatsubst(testname, [versioned], [--versioned])], [0], [], []) ++ AT_CLEANUP])]) +diff --git a/openvswitch-2.14.2/tests/test-hiovs-offload.c b/openvswitch-2.14.2/tests/test-hiovs-offload.c +new file mode 100644 +index 0000000..fb7b6fc +--- /dev/null ++++ b/openvswitch-2.14.2/tests/test-hiovs-offload.c +@@ -0,0 +1,262 @@ ++/* ++ * Copyright (c) 2015 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 ++#include ++#include ++ ++#include "ovstest.h" ++#include "util.h" ++#include "hwoff_init_func.h" ++#include "dpif.h" ++#include "cmap.h" ++#include "dp-packet.h" ++#include "netdev-offload-provider.h" ++ ++struct rte_flow { ++ /* rte_flow mega ufid. */ ++ ovs_u128 mega_ufid; ++ /* Number of packets matched. */ ++ uint64_t sw_packets; ++ /* Number of bytes matched. */ ++ uint64_t sw_bytes; ++ /* Time of rte_flow success offload. */ ++ long long sw_offload_time; ++ struct cmap_node node; ++ /* rte_flow mapped hardware flow. */ ++ struct cmap hw_list; ++}; ++ ++static int mock_hwoff_init_flow_api(struct netdev *netdev) ++{ ++ (void)netdev; ++ return 0; ++} ++ ++static struct rte_flow* mock_hwoff_rte_flow_create(struct netdev *netdev, const struct rte_flow_attr *attr, ++ const struct rte_flow_item *items, ++ const struct rte_flow_action *actions, ++ struct rte_flow_error *error) ++{ ++ struct rte_flow *new_rte_flow = NULL; ++ ++ (void)netdev; ++ (void)attr; ++ (void)items; ++ (void)actions; ++ (void)error; ++ new_rte_flow = (struct rte_flow *)malloc(sizeof(*new_rte_flow)); ++ return new_rte_flow; ++} ++ ++static int mock_hwoff_rte_flow_destroy(struct netdev *netdev, struct rte_flow *rte_flow, struct rte_flow_error *error) ++{ ++ (void)netdev; ++ (void)rte_flow; ++ (void)error; ++ return 0; ++} ++ ++static int mock_hwoff_rte_flow_query_count(struct netdev *netdev, struct rte_flow *rte_flow, ++ struct rte_flow_query_count *query, struct rte_flow_error *error) ++{ ++ (void)netdev; ++ (void)rte_flow; ++ (void)query; ++ (void)error; ++ return 0; ++} ++ ++static bool mock_hwoff_is_hiovs_netdev(const struct netdev *netdev) ++{ ++ (void)netdev; ++ return true; ++} ++ ++static bool mock_hwoff_is_support_offload(const struct netdev *netdev) ++{ ++ (void)netdev; ++ return true; ++} ++ ++static void mock_hwoff_funcs(void) ++{ ++ hwoff_func* funcs = hwoff_get_funcs(); ++ ++ funcs->hwoff_init_flow_api = mock_hwoff_init_flow_api; ++ funcs->hwoff_rte_flow_create = mock_hwoff_rte_flow_create; ++ funcs->hwoff_rte_flow_destroy = mock_hwoff_rte_flow_destroy; ++ funcs->hwoff_rte_flow_query_count = mock_hwoff_rte_flow_query_count; ++ funcs->hwoff_is_hiovs_netdev = mock_hwoff_is_hiovs_netdev; ++ funcs->hwoff_is_support_offload = mock_hwoff_is_support_offload; ++} ++ ++static struct netdev* hiovs_create_fake_netdev(void) ++{ ++ struct netdev *dev; ++ ++ dev = (struct netdev *)malloc(sizeof(struct netdev)); ++ if (!dev) { ++ return NULL; ++ } ++ ++ return dev; ++} ++ ++static void hiovs_fill_offload_info(struct offload_info *tmp_offload_info) ++{ ++ tmp_offload_info->in_port_id = 1; ++ tmp_offload_info->in_port_type = 1; ++ tmp_offload_info->pmd_core_id = 1; ++ tmp_offload_info->modification = false; ++} ++ ++static void hiovs_offload_flow_put(ovs_u128 *ufid, struct netdev *dev) ++{ ++ int ret; ++ uint8_t actions_stub[512]; ++ struct ofpbuf actions; ++ struct dpif_flow_stats stats; ++ struct offload_info tmp_offload_info; ++ struct dp_packet_batch pkt_batch; ++ struct netdev_flow_api *hiovs_class = (struct netdev_flow_api *)&netdev_offload_hiovs; ++ ++ ofpbuf_use_stub(&actions, actions_stub, sizeof(actions_stub)); ++ nl_msg_put_odp_port(&actions, OVS_ACTION_ATTR_OUTPUT, u32_to_odp(1)); ++ ++ tmp_offload_info.pkts_info = &pkt_batch; ++ hiovs_fill_offload_info(&tmp_offload_info); ++ ++ ret = hiovs_class->flow_put(dev, NULL, actions.data, actions.size, ufid, &tmp_offload_info, &stats); ++ if (ret != 0) { ++ ovs_fatal(0, "flow_put fail"); ++ return; ++ } ++ return; ++} ++ ++static void hiovs_fill_ufid(ovs_u128 *ufid, uint32_t value) ++{ ++ ufid->u32[0] = value; ++ ufid->u32[1] = 0; ++ ufid->u32[2] = 0; ++ ufid->u32[3] = 0; ++} ++ ++static void test_hiovs_offload_init_flow_api(struct ovs_cmdl_context *ctx OVS_UNUSED) ++{ ++ int ret; ++ struct netdev *dev = NULL; ++ struct netdev_flow_api *hiovs_class = (struct netdev_flow_api *)&netdev_offload_hiovs; ++ ++ mock_hwoff_funcs(); ++ dev = hiovs_create_fake_netdev(); ++ if (!dev) { ++ ovs_fatal(0, "can't create netdev"); ++ return; ++ } ++ ++ ret = hiovs_class->init_flow_api(dev); ++ if (ret != 0) { ++ ovs_fatal(0, "init_flow_api execute fail"); ++ free(dev); ++ return; ++ } ++ ++ return; ++} ++ ++static void test_hiovs_offload_flow_put(struct ovs_cmdl_context *ctx OVS_UNUSED) ++{ ++ ovs_u128 ufid; ++ struct netdev *dev = NULL; ++ ++ hiovs_fill_ufid(&ufid, 211); ++ mock_hwoff_funcs(); ++ dev = hiovs_create_fake_netdev(); ++ if (!dev) { ++ ovs_fatal(0, "can't create netdev"); ++ return; ++ } ++ ++ hiovs_offload_flow_put(&ufid, dev); ++ free(dev); ++ return; ++} ++ ++static void test_hiovs_offload_flow_delete(struct ovs_cmdl_context *ctx OVS_UNUSED) ++{ ++ ovs_u128 ufid; ++ struct netdev *dev = NULL; ++ struct dpif_flow_stats stats; ++ struct netdev_flow_api *hiovs_class = (struct netdev_flow_api *)&netdev_offload_hiovs; ++ ++ hiovs_fill_ufid(&ufid, 212); ++ mock_hwoff_funcs(); ++ dev = hiovs_create_fake_netdev(); ++ if (!dev) { ++ ovs_fatal(0, "can't create netdev"); ++ return; ++ } ++ ++ hiovs_offload_flow_put(&ufid, dev); ++ hiovs_class->flow_del(dev, &ufid, &stats); ++ free(dev); ++ return; ++} ++ ++static void test_hiovs_offload_flow_get(struct ovs_cmdl_context *ctx OVS_UNUSED) ++{ ++ ovs_u128 ufid; ++ struct netdev *dev = NULL; ++ struct dpif_flow_stats stats; ++ struct dpif_flow_attrs attr; ++ struct netdev_flow_api *hiovs_class = (struct netdev_flow_api *)&netdev_offload_hiovs; ++ ++ hiovs_fill_ufid(&ufid, 212); ++ mock_hwoff_funcs(); ++ dev = hiovs_create_fake_netdev(); ++ if (!dev) { ++ ovs_fatal(0, "can't create netdev"); ++ return; ++ } ++ ++ hiovs_offload_flow_put(&ufid, dev); ++ ++ hiovs_class->flow_get(dev, NULL, NULL, &ufid, &stats, &attr, NULL); ++ free(dev); ++ return; ++} ++ ++static const struct ovs_cmdl_command commands[] = { ++ {"init_flow_api", "init_flow_api", 0, 0, test_hiovs_offload_init_flow_api, OVS_RO}, ++ {"flow_put", "flow_put", 0, 0, test_hiovs_offload_flow_put, OVS_RO}, ++ {"flow_del", "flow_del", 0, 0, test_hiovs_offload_flow_delete, OVS_RO}, ++ {"flow_get", "flow_get", 0, 0, test_hiovs_offload_flow_get, OVS_RO}, ++ {NULL, NULL, 0, 0, NULL, OVS_RO}, ++}; ++ ++static void test_hiovs_offload_main(int argc, char *argv[]) ++{ ++ struct ovs_cmdl_context ctx = { ++ .argc = argc - 1, ++ .argv = argv + 1, ++ }; ++ set_program_name(argv[0]); ++ ovs_cmdl_run_command(&ctx, commands); ++} ++ ++OVSTEST_REGISTER("test-hiovs-offload", test_hiovs_offload_main); +diff --git a/openvswitch-2.14.2/tests/testsuite.at b/openvswitch-2.14.2/tests/testsuite.at +index 7369991..5cc06a8 100644 +--- a/openvswitch-2.14.2/tests/testsuite.at ++++ b/openvswitch-2.14.2/tests/testsuite.at +@@ -77,3 +77,4 @@ m4_include([tests/mcast-snooping.at]) + m4_include([tests/packet-type-aware.at]) + m4_include([tests/nsh.at]) + m4_include([tests/drop-stats.at]) ++m4_include([tests/hiovs-offload.at]) +diff --git a/openvswitch-2.14.2/vswitchd/bridge.c b/openvswitch-2.14.2/vswitchd/bridge.c +index a332517..2b27edd 100644 +--- a/openvswitch-2.14.2/vswitchd/bridge.c ++++ b/openvswitch-2.14.2/vswitchd/bridge.c +@@ -19,6 +19,10 @@ + #include + #include + ++#ifdef HAVE_HWOFF_AGENT ++#include "hwoff_init_func.h" ++#endif ++ + #include "async-append.h" + #include "bfd.h" + #include "bitmap.h" +@@ -553,6 +557,11 @@ bridge_exit(bool delete_datapath) + } + + ovsdb_idl_destroy(idl); ++ ++#ifdef HAVE_HWOFF_AGENT ++ hwoff_clear_pf_access_hugepages(); ++ hwoff_free_hugepages(); ++#endif + } + + /* Looks at the list of managers in 'ovs_cfg' and extracts their remote IP +@@ -3251,6 +3260,9 @@ bridge_run__(void) + } + } + ++extern void rte_adapter_init(void); ++extern void ovs_adapter_init(void); ++ + void + bridge_run(void) + { +@@ -3291,6 +3303,16 @@ bridge_run(void) + netdev_set_flow_api_enabled(&cfg->other_config); + dpdk_init(&cfg->other_config); + userspace_tso_init(&cfg->other_config); ++ ++#ifdef HAVE_HWOFF_AGENT ++ int ret = netdev_offload_hw_init(&cfg->other_config); ++ if (ret == 0) { ++ hwoff_func* funcs = hwoff_get_funcs(); ++ if (funcs->hwoff_parse_ovs_other_config) { ++ funcs->hwoff_parse_ovs_other_config(&cfg->other_config); ++ } ++ } ++#endif + } + + /* Initialize the ofproto library. This only needs to run once, but -- Gitee