diff --git a/openvswitch-2.14.2-20241009.patch b/openvswitch-2.14.2-20241009.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a244f52cbb9b23bece00350202d78a9bf772d1ee
--- /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