From 4a5da01a47382697ddc4cd7fa0707aae849d1156 Mon Sep 17 00:00:00 2001 From: yuan-chuxiang <690654847@qq.com> Date: Fri, 26 Sep 2025 10:05:21 +0800 Subject: [PATCH] ovs3.3.0-20250930 --- .../openvswitch-3.3.0-20250930.patch | 4434 +++++++++++++++++ 1 file changed, 4434 insertions(+) create mode 100644 openvswitch3.3.0/openvswitch-3.3.0-20250930.patch diff --git a/openvswitch3.3.0/openvswitch-3.3.0-20250930.patch b/openvswitch3.3.0/openvswitch-3.3.0-20250930.patch new file mode 100644 index 0000000..517efa4 --- /dev/null +++ b/openvswitch3.3.0/openvswitch-3.3.0-20250930.patch @@ -0,0 +1,4434 @@ +diff --git a/Makefile.in b/Makefile.in +index e89e082..33bdc0c 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -258,15 +258,17 @@ 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_26 = \ ++@HAVE_HWOFF_AGENT_TRUE@am__append_26 = -lhwoffagent ++ ++@DPDK_NETDEV_TRUE@am__append_27 = \ + @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_27 = \ ++@DPDK_NETDEV_FALSE@am__append_28 = \ + @DPDK_NETDEV_FALSE@ lib/dpdk-stub.c + +-@WIN32_TRUE@am__append_28 = \ ++@WIN32_TRUE@am__append_29 = \ + @WIN32_TRUE@ lib/dpif-netlink.c \ + @WIN32_TRUE@ lib/dpif-netlink.h \ + @WIN32_TRUE@ lib/dpif-netlink-rtnl.h \ +@@ -281,42 +283,42 @@ 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_29 = lib/async-append-aio.c +-@HAVE_POSIX_AIO_FALSE@am__append_30 = lib/async-append-null.c +-@HAVE_IF_DL_TRUE@am__append_31 = \ ++@HAVE_POSIX_AIO_TRUE@am__append_30 = lib/async-append-aio.c ++@HAVE_POSIX_AIO_FALSE@am__append_31 = lib/async-append-null.c ++@HAVE_IF_DL_TRUE@am__append_32 = \ + @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_32 = lib/stream-ssl.c lib/dhparams.c +-@HAVE_OPENSSL_FALSE@am__append_33 = lib/stream-nossl.c +-@HAVE_UNBOUND_TRUE@am__append_34 = lib/dns-resolve.c +-@HAVE_UNBOUND_FALSE@am__append_35 = lib/dns-resolve-stub.c +-@WIN32_TRUE@am__append_36 = ${PTHREAD_LIBS} +-@LINUX_TRUE@am__append_37 = utilities/nlmon +-@WIN32_FALSE@am__append_38 = \ ++@HAVE_OPENSSL_TRUE@am__append_33 = lib/stream-ssl.c lib/dhparams.c ++@HAVE_OPENSSL_FALSE@am__append_34 = lib/stream-nossl.c ++@HAVE_UNBOUND_TRUE@am__append_35 = lib/dns-resolve.c ++@HAVE_UNBOUND_FALSE@am__append_36 = lib/dns-resolve-stub.c ++@WIN32_TRUE@am__append_37 = ${PTHREAD_LIBS} ++@LINUX_TRUE@am__append_38 = utilities/nlmon ++@WIN32_FALSE@am__append_39 = \ + @WIN32_FALSE@ tests/test-unix-socket.c + +-@LINUX_TRUE@am__append_39 = \ ++@LINUX_TRUE@am__append_40 = \ + @LINUX_TRUE@ tests/test-netlink-conntrack.c \ + @LINUX_TRUE@ tests/test-netlink-policy.c + +-@HAVE_OPENSSL_TRUE@am__append_40 = $(TESTPKI_FILES) +-@HAVE_OPENSSL_TRUE@am__append_41 = $(TESTPKI_FILES) tests/ovs-pki.log +-@HAVE_OPENSSL_TRUE@am__append_42 = clean-pki ++@HAVE_OPENSSL_TRUE@am__append_41 = $(TESTPKI_FILES) ++@HAVE_OPENSSL_TRUE@am__append_42 = $(TESTPKI_FILES) tests/ovs-pki.log ++@HAVE_OPENSSL_TRUE@am__append_43 = 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_43 = include/openvswitch/libcxxtest.la +-@HAVE_CXX_TRUE@am__append_44 = include/openvswitch/cxxtest.cc +-@HAVE_DOT_TRUE@am__append_45 = vswitchd/vswitch.gv vswitchd/vswitch.pic +-@HAVE_DOT_TRUE@am__append_46 = vtep/vtep.gv vtep/vtep.pic +-@WIN32_TRUE@am__append_47 = $(srcdir)/datapath-windows/include/OvsDpInterface.h ++@HAVE_CXX_TRUE@am__append_44 = include/openvswitch/libcxxtest.la ++@HAVE_CXX_TRUE@am__append_45 = include/openvswitch/cxxtest.cc ++@HAVE_DOT_TRUE@am__append_46 = vswitchd/vswitch.gv vswitchd/vswitch.pic ++@HAVE_DOT_TRUE@am__append_47 = vtep/vtep.gv vtep/vtep.pic ++@WIN32_TRUE@am__append_48 = $(srcdir)/datapath-windows/include/OvsDpInterface.h + subdir = . + ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 + am__aclocal_m4_deps = $(top_srcdir)/m4/absolute-header.m4 \ +@@ -410,7 +412,8 @@ am__v_lt_1 = + am__DEPENDENCIES_1 = + @WIN32_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) + lib_libopenvswitch_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ +- $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) $(am__append_16) ++ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ ++ $(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 \ +@@ -419,6 +422,8 @@ 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/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 \ +@@ -578,8 +583,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/cooperative-multitasking.lo lib/coverage.lo lib/cpu.lo \ + lib/crc32c.lo lib/csum.lo lib/ct-dpif.lo lib/daemon.lo \ +@@ -962,8 +967,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-fpool.Plo lib/$(DEPDIR)/id-pool.Plo \ +- lib/$(DEPDIR)/if-notifier-bsd.Plo \ ++ lib/$(DEPDIR)/id-fpool.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 \ +@@ -1580,7 +1585,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_47) ++ include/odp-netlink-macros.h $(OVSIDL_BUILT) $(am__append_48) + + # Clean up generated files from older OVS versions. (This is important so that + # #include "vswitch-idl.h" doesn't get the wrong copy.) +@@ -1600,11 +1605,11 @@ CLEANFILES = all-gitfiles missing-distfiles distfiles $(am__append_7) \ + utilities/ovs-test utilities/ovs-vlan-test \ + utilities/ovs-vsctl.8 utilities/bugtool/ovs-bugtool \ + utilities/bugtool/ovs-bugtool.8 $(valgrind_wrappers) \ +- $(am__append_41) include/odp-netlink.h \ +- include/odp-netlink-macros.h $(HSTAMP_FILES) $(am__append_44) \ ++ $(am__append_42) include/odp-netlink.h \ ++ include/odp-netlink-macros.h $(HSTAMP_FILES) $(am__append_45) \ + cxx-check debian/copyright debian/control \ + ipsec/ovs-monitor-ipsec vswitchd/ovs-vswitchd.8 \ +- $(am__append_45) vswitchd/ovs-vswitchd.conf.db.5 \ ++ $(am__append_46) 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 \ + ovsdb/ovsdb-server.1 ovsdb/ovsdb-idlc $(OVSIDL_BUILT) \ +@@ -1612,7 +1617,7 @@ CLEANFILES = all-gitfiles missing-distfiles distfiles $(am__append_7) \ + ovsdb/_server.ovsschema.stamp ovsdb/ovsdb-server.5 \ + ovsdb/local-config.ovsschema.stamp ovsdb/ovsdb.local-config.5 \ + python/ovs/dirs.py python/ovs/flow/ofp_fields.py \ +- vtep/vtep-ctl.8 vtep/ovs-vtep $(am__append_46) vtep/vtep.5 \ ++ vtep/vtep-ctl.8 vtep/ovs-vtep $(am__append_47) vtep/vtep.5 \ + vtep/vtep.ovsschema.stamp \ + $(srcdir)/datapath-windows/include/OvsDpInterface.h \ + selinux/openvswitch-custom.te selinux/openvswitch-custom.pp \ +@@ -1621,7 +1626,7 @@ CLEANFILES = all-gitfiles missing-distfiles distfiles $(am__append_7) \ + # 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_42) ++ $(am__append_43) + DISTCLEANFILES = tests/atconfig tests/atlocal \ + rhel/usr_lib_systemd_system_ovs-vswitchd.service + PYCOV_CLEAN_FILES = build-aux/check-structs,cover \ +@@ -2011,7 +2016,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_43) ++noinst_LTLIBRARIES = $(am__append_44) + noinst_man_MANS = + + # ovsdb-idlc +@@ -2048,7 +2053,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_40) ++check_DATA = $(am__append_41) + check_SCRIPTS = utilities/ovs-appctl-bashcomp.bash \ + utilities/ovs-vsctl-bashcomp.bash tests/atlocal + pkgconfig_DATA = lib/libopenvswitch.pc lib/libsflow.pc \ +@@ -2306,7 +2311,8 @@ extract_stem_and_section = \ + test -n "$$mandir" || { echo "unknown directory for manpage section $$section"; continue; } + + lib_libopenvswitch_la_LIBADD = $(SSL_LIBS) $(CAPNG_LDADD) \ +- $(am__append_14) $(am__append_16) ++ $(am__append_14) $(am__append_16) \ ++ $(am__append_26) + lib_libopenvswitch_la_LDFLAGS = \ + $(OVS_LTINFO) \ + -Wl,--version-script=$(top_builddir)/lib/libopenvswitch.sym \ +@@ -2333,6 +2339,7 @@ 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/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 \ +@@ -2437,10 +2444,10 @@ lib_libopenvswitch_la_SOURCES = lib/aes128.c lib/aes128.h \ + lib/lldp/lldp.c lib/lldp/lldp-const.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_20) $(am__append_21) \ +- $(am__append_24) $(am__append_25) $(am__append_26) \ ++ $(am__append_24) $(am__append_25) \ + $(am__append_27) $(am__append_28) $(am__append_29) \ + $(am__append_30) $(am__append_31) $(am__append_32) \ +- $(am__append_33) lib/dns-resolve.h $(am__append_34) \ ++ $(am__append_33) lib/dns-resolve.h $(am__append_34) $(am__append_35) \ + $(am__append_35) + nodist_lib_libopenvswitch_la_SOURCES = \ + lib/dirs.c \ +@@ -2761,7 +2768,7 @@ tests_ovstest_SOURCES = tests/ovstest.c tests/ovstest.h \ + tests/test-skiplist.c tests/test-stp.c tests/test-unixctl.c \ + tests/test-util.c tests/test-uuid.c tests/test-uuidset.c \ + tests/test-bitmap.c tests/test-vconn.c tests/test-aa.c \ +- tests/test-stopwatch.c $(am__append_38) $(am__append_39) ++ tests/test-stopwatch.c $(am__append_39) $(am__append_40) + tests_ovstest_LDADD = lib/libopenvswitch.la + tests_test_stream_SOURCES = tests/test-stream.c + tests_test_stream_LDADD = lib/libopenvswitch.la +@@ -3362,6 +3369,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) \ +@@ -4412,6 +4421,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-fpool.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 +@@ -5863,6 +5873,7 @@ distclean: distclean-am + -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-fpool.Plo + -rm -f lib/$(DEPDIR)/id-pool.Plo + -rm -f lib/$(DEPDIR)/if-notifier-bsd.Plo +@@ -6269,6 +6280,7 @@ maintainer-clean: maintainer-clean-am + -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-fpool.Plo + -rm -f lib/$(DEPDIR)/id-pool.Plo + -rm -f lib/$(DEPDIR)/if-notifier-bsd.Plo +diff --git a/acinclude.m4 b/acinclude.m4 +index f1ba046..99f61df 100644 +--- a/acinclude.m4 ++++ b/acinclude.m4 +@@ -497,6 +497,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 Checks for net/if_dl.h. + dnl + dnl (We use this as a proxy for checking whether we're building on FreeBSD +@@ -562,7 +578,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 +@@ -617,7 +633,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/configure.ac b/configure.ac +index 05afbb9..ccdf6f8 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -201,6 +201,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([CFLAGS]) + AC_SUBST([OVS_CFLAGS]) +diff --git a/include/openflow/openflow-1.3.h b/include/openflow/openflow-1.3.h +index 2567b50..3ae146e 100644 +--- a/include/openflow/openflow-1.3.h ++++ b/include/openflow/openflow-1.3.h +@@ -156,7 +156,10 @@ enum ofp13_meter_flags { + OFPMF13_KBPS = 1 << 0, /* Rate value in kb/s (kilo-bit per second). */ + OFPMF13_PKTPS = 1 << 1, /* Rate value in packet/sec. */ + OFPMF13_BURST = 1 << 2, /* Do burst size. */ +- OFPMF13_STATS = 1 << 3 /* Collect statistics. */ ++ OFPMF13_STATS = 1 << 3, /* Collect statistics. */ ++#ifdef HAVE_HWOFF_AGENT ++ OFPMF13_OFFLOAD = 1 << 4 ++#endif + }; + + /* Meter band types */ +@@ -328,6 +331,24 @@ struct ofp13_meter_band_stats { + OFP_ASSERT(sizeof(struct ofp13_meter_band_stats) == 16); + + /* Body of reply to OFPMP13_METER request. Meter statistics. */ ++#ifdef HAVE_HWOFF_AGENT ++struct ofp13_meter_stats { ++ ovs_be32 meter_id; /* Meter instance. */ ++ ovs_be16 len; /* Length in bytes of this stats. */ ++ uint8_t pad[6]; ++ ovs_be32 flow_count; /* Number of flows bound to meter. */ ++ ovs_be64 packet_in_count; /* Number of packets in input. */ ++ ovs_be64 byte_in_count; /* Number of bytes in input. */ ++ ovs_be32 duration_sec; /* Time meter has been alive in seconds. */ ++ ovs_be32 duration_nsec; /* Time meter has been alive in nanoseconds ++ beyond duration_sec. */ ++ ovs_be64 n_pkts_dropped; ++ ovs_be64 n_bytes_dropped; ++ struct ofp13_meter_band_stats band_stats[0]; /* The band_stats length is ++ inferred from the length field. */ ++}; ++OFP_ASSERT(sizeof(struct ofp13_meter_stats) == 56); ++#else + struct ofp13_meter_stats { + ovs_be32 meter_id; /* Meter instance. */ + ovs_be16 len; /* Length in bytes of this stats. */ +@@ -342,6 +363,7 @@ struct ofp13_meter_stats { + inferred from the length field. */ + }; + OFP_ASSERT(sizeof(struct ofp13_meter_stats) == 40); ++#endif + + /* Body of reply to OFPMP13_METER_CONFIG request. Meter configuration. */ + struct ofp13_meter_config { +diff --git a/include/openvswitch/ofp-meter.h b/include/openvswitch/ofp-meter.h +index 6776eae..a35f9a9 100644 +--- a/include/openvswitch/ofp-meter.h ++++ b/include/openvswitch/ofp-meter.h +@@ -88,6 +88,10 @@ struct ofputil_meter_stats { + uint32_t duration_nsec; + uint16_t n_bands; + struct ofputil_meter_band_stats *bands; ++#ifdef HAVE_HWOFF_AGENT ++ uint64_t n_num_dropped_byte; ++ uint64_t n_num_dropped_pkts; ++#endif + }; + + void ofputil_append_meter_stats(struct ovs_list *replies, +diff --git a/lib/adapter/ovs_adapter.h b/lib/adapter/ovs_adapter.h +new file mode 100644 +index 0000000..e6b7ad4 +--- /dev/null ++++ b/lib/adapter/ovs_adapter.h +@@ -0,0 +1,61 @@ ++/* ++ * Copyright (c) 2022-2025. 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: 2025-04-18 ++ */ ++ ++#ifndef _OVS_ADAPTER_H ++#define _OVS_ADAPTER_H ++ ++#include ++#include "conntrack-private.h" ++#include "conntrack-tp.h" ++#include "conntrack.h" ++#include "dp-packet.h" ++#include "dpif-netdev.h" ++#include "netdev-provider.h" ++#include "packets.h" ++ ++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); ++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); ++ ++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); ++ ++const char* netdev_class_get_type(struct netdev_class *netdev_class); ++ ++struct conn* pkt_metadata_get_conn(struct pkt_metadata *md); ++bool pkt_metadata_get_reply(struct pkt_metadata *md); ++ ++#endif +\ No newline at end of file +diff --git a/lib/adapter/ovs_dp_adapter.c b/lib/adapter/ovs_dp_adapter.c +new file mode 100644 +index 0000000..bf2ceb8 +--- /dev/null ++++ b/lib/adapter/ovs_dp_adapter.c +@@ -0,0 +1,43 @@ ++/* ++ * Copyright (c) 2022-2025. 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: 2025-04-18 ++ */ ++ ++#include ++#include "dp-packet.h" ++#include "dpif-netdev.h" ++#include "ovs_adapter.h" ++ ++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/lib/adapter/ovs_netdev_adapter.c b/lib/adapter/ovs_netdev_adapter.c +new file mode 100644 +index 0000000..482f87d +--- /dev/null ++++ b/lib/adapter/ovs_netdev_adapter.c +@@ -0,0 +1,27 @@ ++/* ++ * Copyright (c) 2022-2025. 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: 2025-04-18 ++ */ ++ ++#include ++#include "netdev-provider.h" ++#include "ovs_adapter.h" ++ ++const char* netdev_class_get_type(struct netdev_class *netdev_class) ++{ ++ return netdev_class->type; ++} +\ No newline at end of file +diff --git a/lib/adapter/ovs_packet_adapter.c b/lib/adapter/ovs_packet_adapter.c +new file mode 100644 +index 0000000..1746e61 +--- /dev/null ++++ b/lib/adapter/ovs_packet_adapter.c +@@ -0,0 +1,32 @@ ++/* ++ * Copyright (c) 2022-2025. 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: 2025-04-18 ++ */ ++ ++#include ++#include "packets.h" ++#include "ovs_adapter.h" ++ ++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; ++} +diff --git a/lib/automake.mk b/lib/automake.mk +index 78d6e65..2c1c0b1 100644 +--- a/lib/automake.mk ++++ b/lib/automake.mk +@@ -410,7 +410,10 @@ 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_dp_adapter.c \ ++ lib/adapter/ovs_netdev_adapter.c \ ++ lib/adapter/ovs_packet_adapter.c + + if WIN32 + lib_libopenvswitch_la_SOURCES += \ +@@ -499,6 +502,12 @@ 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 ++endif ++ + if DPDK_NETDEV + lib_libopenvswitch_la_SOURCES += \ + lib/dpdk.c \ +diff --git a/lib/dp-packet.h b/lib/dp-packet.h +index 2fa17d8..295e72a 100644 +--- a/lib/dp-packet.h ++++ b/lib/dp-packet.h +@@ -704,6 +704,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) + { +@@ -727,6 +757,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) +@@ -992,6 +1023,7 @@ dp_packet_batch_clone(struct dp_packet_batch *dst, + struct dp_packet *pkt_clone; + + pkt_clone = dp_packet_clone_with_headroom(packet, headroom); ++ pkt_clone->mbuf.dynfield1[7] = packet->mbuf.dynfield1[7]; + dp_packet_batch_add(dst, pkt_clone); + } + dst->trunc = src->trunc; +@@ -1244,7 +1276,7 @@ dp_packet_hwol_tx_ip_csum(const struct dp_packet *p) + static inline void + dp_packet_hwol_set_tx_ip_csum(struct dp_packet *p) + { +- *dp_packet_ol_flags_ptr(p) |= DP_PACKET_OL_TX_IP_CKSUM; ++ //*dp_packet_ol_flags_ptr(p) |= DP_PACKET_OL_TX_IP_CKSUM; + } + + static inline void +@@ -1258,7 +1290,7 @@ dp_packet_hwol_reset_tx_ip_csum(struct dp_packet *p) + static inline void + dp_packet_hwol_set_csum_tcp(struct dp_packet *b) + { +- *dp_packet_ol_flags_ptr(b) |= DP_PACKET_OL_TX_TCP_CKSUM; ++ //*dp_packet_ol_flags_ptr(b) |= DP_PACKET_OL_TX_TCP_CKSUM; + } + + /* Mark packet 'b' for UDP checksum offloading. It implies that either +@@ -1266,7 +1298,7 @@ dp_packet_hwol_set_csum_tcp(struct dp_packet *b) + static inline void + dp_packet_hwol_set_csum_udp(struct dp_packet *b) + { +- *dp_packet_ol_flags_ptr(b) |= DP_PACKET_OL_TX_UDP_CKSUM; ++ //*dp_packet_ol_flags_ptr(b) |= DP_PACKET_OL_TX_UDP_CKSUM; + } + + /* Mark packet 'b' for SCTP checksum offloading. It implies that either +@@ -1283,7 +1315,7 @@ dp_packet_hwol_set_csum_sctp(struct dp_packet *b) + static inline void + dp_packet_hwol_set_tcp_seg(struct dp_packet *b) + { +- *dp_packet_ol_flags_ptr(b) |= DP_PACKET_OL_TX_TCP_SEG; ++ //*dp_packet_ol_flags_ptr(b) |= DP_PACKET_OL_TX_TCP_SEG; + } + + /* Mark packet 'b' for tunnel GENEVE offloading. */ +diff --git a/lib/dpdk.c b/lib/dpdk.c +index d76d53f..e0238aa 100644 +--- a/lib/dpdk.c ++++ b/lib/dpdk.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -41,6 +42,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); + +@@ -287,6 +299,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) { +@@ -310,6 +323,109 @@ malloc_dump_stats_wrapper(FILE *stream) + rte_malloc_dump_stats(stream, 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); ++ if (dir_barray == NULL) { ++ VLOG_ERR("failed to open dir with file %s", path); ++ rmdir(path); ++ return ; ++ } ++ 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) { ++ snprintf(path, sizeof(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) { ++ snprintf(path, sizeof(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) + { +@@ -381,6 +497,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]); +@@ -442,7 +562,7 @@ dpdk_init__(const struct smap *ovs_other_config) + + /* Finally, register the dpdk classes */ + netdev_dpdk_register(ovs_other_config); +- netdev_register_flow_api_provider(&netdev_offload_dpdk); ++ //netdev_register_flow_api_provider(&netdev_offload_dpdk); + return true; + } + +diff --git a/lib/dpdk.h b/lib/dpdk.h +index 1b790e6..d592766 100644 +--- a/lib/dpdk.h ++++ b/lib/dpdk.h +@@ -42,4 +42,9 @@ bool dpdk_available(void); + void print_dpdk_version(void); + void dpdk_status(const struct ovsrec_open_vswitch *); + ++#ifdef HAVE_HWOFF_AGENT ++void hwoff_free_hugepages(void); ++void hwoff_clear_pf_access_hugepages(void); ++#endif ++ + #endif /* dpdk.h */ +diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c +index 46e24d2..87f9215 100644 +--- a/lib/dpif-netdev.c ++++ b/lib/dpif-netdev.c +@@ -34,6 +34,12 @@ + #include + #include + ++#ifdef HAVE_HWOFF_AGENT ++#include "hwoff_init_func.h" ++#include "mac-learning.h" ++#include "rte_mtr.h" ++#endif ++ + #include "bitmap.h" + #include "ccmap.h" + #include "cmap.h" +@@ -207,9 +213,13 @@ static void dpcls_insert(struct dpcls *, struct dpcls_rule *, + static void dpcls_remove(struct dpcls *, struct dpcls_rule *); + + /* Set of supported meter flags */ ++#ifdef HAVE_HWOFF_AGENT ++#define DP_SUPPORTED_METER_FLAGS_MASK \ ++ (OFPMF13_STATS | OFPMF13_PKTPS | OFPMF13_KBPS | OFPMF13_BURST | OFPMF13_OFFLOAD) ++#else + #define DP_SUPPORTED_METER_FLAGS_MASK \ + (OFPMF13_STATS | OFPMF13_PKTPS | OFPMF13_KBPS | OFPMF13_BURST) +- ++#endif + /* Set of supported meter band types */ + #define DP_SUPPORTED_METER_BAND_TYPES \ + ( 1 << OFPMBT13_DROP ) +@@ -342,6 +352,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 struct dp_netdev_port *dp_netdev_lookup_port(const struct dp_netdev *dp, +@@ -488,6 +503,15 @@ struct dp_netdev_port { + enum txq_req_mode txq_requested_mode; + }; + ++#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 ++ + static bool dp_netdev_flow_ref(struct dp_netdev_flow *); + static int dpif_netdev_flow_from_nlattrs(const struct nlattr *, uint32_t, + struct flow *, bool); +@@ -652,6 +676,9 @@ 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); ++static void * ++dp_netdev_flow_offload_main(void *data OVS_UNUSED); ++ + + static void dp_netdev_simple_match_insert(struct dp_netdev_pmd_thread *pmd, + struct dp_netdev_flow *flow) +@@ -2357,6 +2384,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) +@@ -2776,6 +2822,124 @@ dp_netdev_offload_flow_enqueue(struct dp_offload_thread_item *item) + dp_netdev_append_offload(item, tid); + } + ++#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_type = HWOFF_PORT_TYPE_HIOVS; ++ } else { ++ 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_offload_thread_item *item) ++{ ++ int ret; ++ struct netdev *dev = NULL; ++ hwoff_func* funcs = hwoff_get_funcs(); ++ struct dp_netdev *dp = item->dp; ++ struct dp_netdev_flow *flow = item->data->flow.flow; ++ odp_port_t in_port = flow->flow.in_port.odp_port; ++ const char *dpif_type_str = dpif_normalize_type(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_rwlock_rdlock(&dp->port_rwlock); ++ ret = netdev_flow_del(dev, &flow->mega_ufid, NULL); ++ ovs_rwlock_unlock(&dp->port_rwlock); ++ netdev_close(dev); ++ return ret; ++} ++ ++static int ++dp_netdev_flow_offload_put(struct dp_offload_thread_item *item) ++{ ++ /* We do offload in pmd thread using dp_netdev_flow_offload_add, here we just process modification. ++ We process modification just same as delete, thren packet will upcall then reoffload. ++ */ ++ struct dp_offload_flow_item *offload = &item->data->flow; ++ bool modification = offload->op == DP_NETDEV_FLOW_OFFLOAD_OP_MOD; ++ ++ if (!modification) { ++ return 0; ++ } ++ ++ return dp_netdev_flow_offload_del(item); ++} ++ ++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_offload_thread_item *item) + { +@@ -2837,6 +3001,9 @@ dp_netdev_flow_offload_put(struct dp_offload_thread_item *item) + } + } + info.flow_mark = mark; ++#ifdef HAVE_HWOFF_AGENT ++ info.pmd = NULL; ++#endif + info.orig_in_port = offload->orig_in_port; + + port = netdev_ports_get(in_port, dpif_type_str); +@@ -2872,6 +3039,7 @@ err_free: + } + return -1; + } ++#endif + + static void + dp_offload_flow(struct dp_offload_thread_item *item) +@@ -3217,10 +3385,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; + } + +@@ -3829,6 +4015,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; + +@@ -4208,19 +4397,87 @@ 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)); ++ ++ + ccmap_inc(&pmd->n_flows, odp_to_u32(in_port)); + + if (dp_netdev_flow_is_simple_match(match)) { + dp_netdev_simple_match_insert(pmd, flow); + } + ++#ifndef HAVE_HWOFF_AGENT + queue_netdev_flow_put(pmd, flow, match, actions, actions_len, + DP_NETDEV_FLOW_OFFLOAD_OP_ADD); ++#endif + log_netdev_flow_change(flow, match, NULL, actions, actions_len); + + 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, +@@ -4231,7 +4488,6 @@ flow_put_on_pmd(struct dp_netdev_pmd_thread *pmd, + { + struct dp_netdev_flow *netdev_flow = NULL; + int error = 0; +- + if (stats) { + memset(stats, 0, sizeof *stats); + } +@@ -4302,6 +4558,9 @@ flow_put_on_pmd(struct dp_netdev_pmd_thread *pmd, + + exit: + ovs_mutex_unlock(&pmd->flow_mutex); ++#ifdef HAVE_HWOFF_AGENT ++ dp_netdev_clear_flow_by_rarp(pmd, netdev_flow); ++#endif + return error; + } + +@@ -4396,6 +4655,39 @@ 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) ++{ ++ struct dp_netdev *dp = pmd->dp; ++ 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, +@@ -4408,6 +4700,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); + } +@@ -4416,7 +4711,6 @@ flow_del_on_pmd(struct dp_netdev_pmd_thread *pmd, + error = ENOENT; + } + ovs_mutex_unlock(&pmd->flow_mutex); +- + return error; + } + +@@ -5612,6 +5906,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, +@@ -5655,12 +6019,28 @@ dp_netdev_process_rxq_port(struct dp_netdev_pmd_thread *pmd, + } + } + +- /* Process packet batch. */ ++#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 { ++ int ret = pmd->netdev_input_func(pmd, &batch, port_no); ++ if (ret) { ++ dp_netdev_input(pmd, &batch, port_no); ++ } ++ } ++#else + int ret = pmd->netdev_input_func(pmd, &batch, port_no); + if (ret) { + 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); +@@ -6234,12 +6614,23 @@ sched_numa_list_schedule(struct sched_numa_list *numa_list, + bool start_logged = false; + size_t n_numa; + ++#ifdef HAVE_HWOFF_AGENT ++ /* share upcall queues just will be assigned once to avoid invalid poll*/ ++ bool share_upcall_scheduled = false; ++#endif + /* For each port. */ + 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 each rxq on the port. */ + for (int qid = 0; qid < port->n_rxq; qid++) { + struct dp_netdev_rxq *rxq = &port->rxqs[qid]; +@@ -6305,6 +6696,11 @@ sched_numa_list_schedule(struct sched_numa_list *numa_list, + rxqs[n_rxqs++] = rxq; + } + } ++#ifdef HAVE_HWOFF_AGENT ++ if (is_share_upcall_dev) { ++ share_upcall_scheduled = true; ++ } ++#endif + } + + if (n_rxqs > 1 && algo != SCHED_ROUNDROBIN) { +@@ -7666,6 +8062,42 @@ dpif_netdev_meter_set(struct dpif *dpif, ofproto_meter_id meter_id, + dp_meter_detach_free(&dp->meters, mid); /* Free existing meter, if any. */ + dp_meter_attach(&dp->meters, meter); + ++#ifdef HAVE_HWOFF_AGENT ++ int ret; ++ if (meter->flags & OFPMF13_OFFLOAD) { ++ struct rte_mtr_meter_profile profile = {0}; ++ if (config->n_bands > 2) { ++ ovs_mutex_unlock(&dp->meters_lock); ++ return EINVAL; ++ } ++ ++ hwoff_func* funcs = hwoff_get_funcs(); ++ if (funcs->hwoff_set_flow_qos == NULL) { ++ ovs_mutex_unlock(&dp->meters_lock); ++ return EINVAL; ++ } ++ ++ if (config->n_bands == 1) { ++ profile.trtcm_rfc2698.cir = meter->bands[0].rate; ++ profile.trtcm_rfc2698.cbs = meter->bands[0].burst_size; ++ profile.trtcm_rfc2698.pir = meter->bands[0].rate; ++ profile.trtcm_rfc2698.pbs = meter->bands[0].burst_size; ++ } else if (config->n_bands == 2) { ++ profile.trtcm_rfc2698.cir = meter->bands[1].rate; ++ profile.trtcm_rfc2698.cbs = meter->bands[1].burst_size; ++ profile.trtcm_rfc2698.pir = meter->bands[0].rate; ++ profile.trtcm_rfc2698.pbs = meter->bands[0].burst_size; ++ } ++ ++ profile.packet_mode = ((config->flags & OFPMF13_PKTPS) != 0); ++ ++ ret = funcs->hwoff_set_flow_qos(mid, &profile, config->meter_id); ++ if (ret != 0) { ++ ovs_mutex_unlock(&dp->meters_lock); ++ return -1; ++ } ++ } ++#endif + ovs_mutex_unlock(&dp->meters_lock); + + return 0; +@@ -7679,6 +8111,7 @@ dpif_netdev_meter_get(const struct dpif *dpif, + struct dp_netdev *dp = get_dp_netdev(dpif); + uint32_t meter_id = meter_id_.uint32; + struct dp_meter *meter; ++ int retval = 0; + + if (meter_id >= MAX_METERS) { + return EFBIG; +@@ -7689,6 +8122,23 @@ dpif_netdev_meter_get(const struct dpif *dpif, + return ENOENT; + } + ++#ifdef HAVE_HWOFF_AGENT ++ if (meter != NULL && (meter->flags & OFPMF13_OFFLOAD)) { ++ struct rte_mtr_stats stat; ++ hwoff_func* funcs = hwoff_get_funcs(); ++ if (funcs->hwoff_query_flow_qos_stats== NULL) { ++ goto done; ++ } ++ ++ retval = funcs->hwoff_query_flow_qos_stats(meter_id, &stat); ++ if (retval == 0 && stats != NULL) { ++ stats->n_num_dropped_byte = stat.n_bytes_dropped; ++ stats->n_num_dropped_pkts = stat.n_pkts_dropped; ++ goto done; ++ } ++ } ++#endif ++ + if (stats) { + int i = 0; + +@@ -7704,6 +8154,7 @@ dpif_netdev_meter_get(const struct dpif *dpif, + stats->n_bands = i; + } + ++done: + return 0; + } + +@@ -7720,6 +8171,24 @@ dpif_netdev_meter_del(struct dpif *dpif, + uint32_t meter_id = meter_id_.uint32; + + ovs_mutex_lock(&dp->meters_lock); ++#ifdef HAVE_HWOFF_AGENT ++ int ret; ++ const struct dp_meter *meter = dp_meter_lookup(&dp->meters, meter_id); ++ ++ if (meter != NULL && (meter->flags & OFPMF13_OFFLOAD)) { ++ hwoff_func* funcs = hwoff_get_funcs(); ++ if (funcs->hwoff_del_flow_qos == NULL) { ++ error = ENOENT; ++ goto end; ++ } ++ ++ ret = funcs->hwoff_del_flow_qos(meter_id_.uint32); ++ if (ret != 0) { ++ error = ENOENT; ++ } ++ } ++ end: ++#endif + dp_meter_detach_free(&dp->meters, meter_id); + ovs_mutex_unlock(&dp->meters_lock); + } +@@ -7886,7 +8355,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); +@@ -8224,6 +8705,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; +@@ -8231,6 +8713,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, +@@ -8254,6 +8737,11 @@ packet_batch_per_flow_init(struct packet_batch_per_flow *batch, + batch->tcp_flags = 0; + } + ++static inline bool is_rarp(const struct flow *flow) ++{ ++ return (flow->dl_type == htons(ETH_TYPE_RARP)); ++} ++ + static inline void + packet_batch_per_flow_execute(struct packet_batch_per_flow *batch, + struct dp_netdev_pmd_thread *pmd) +@@ -8267,6 +8755,11 @@ 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); + } +@@ -8704,6 +9197,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. */ +@@ -8774,6 +9270,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; + } + +@@ -8938,6 +9437,16 @@ dpif_netdev_register_upcall_cb(struct dpif *dpif, upcall_callback *cb, + 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, + bool purge) +@@ -9019,6 +9528,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, +@@ -9173,6 +9697,12 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_, + struct tx_port *p; + uint32_t packet_count, packets_dropped; + ++#ifdef HAVE_HWOFF_AGENT ++ struct dp_meter *meter; ++ struct rte_flow_action_meter *meter_usr_data; ++ uint32_t meter_id; ++#endif ++ + switch ((enum ovs_action_attr)type) { + case OVS_ACTION_ATTR_OUTPUT: + dp_execute_output_action(pmd, packets_, should_steal, +@@ -9454,8 +9984,23 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_, + } + + case OVS_ACTION_ATTR_METER: ++#ifdef HAVE_HWOFF_AGENT ++ meter_id = nl_attr_get_u32(a); ++ meter = dp_meter_lookup(&dp->meters, meter_id); ++ if (meter != NULL && (meter->flags & OFPMF13_OFFLOAD)) { ++ packet_count = dp_packet_batch_size(packets_); ++ for (int i = 0; i < packet_count; i++) { ++ meter_usr_data = (struct rte_flow_action_meter *)(&packets_->packets[i]->mbuf.dynfield1[8]); ++ meter_usr_data->mtr_id = meter_id + 1; ++ } ++ } else { ++ dp_netdev_run_meter(pmd->dp, packets_, meter_id, ++ pmd->ctx.now / 1000); ++ } ++#else + dp_netdev_run_meter(pmd->dp, packets_, nl_attr_get_u32(a), + pmd->ctx.now / 1000); ++#endif + break; + + case OVS_ACTION_ATTR_PUSH_VLAN: +@@ -9491,6 +10036,14 @@ dp_netdev_execute_actions(struct dp_netdev_pmd_thread *pmd, + const struct nlattr *actions, size_t actions_len) + { + struct dp_netdev_execute_aux aux = { pmd, flow }; ++ for (uint32_t i = 0; i < packets->count; i++) { ++ struct dp_packet *packet = packets->packets[i]; ++ struct flow flow = {0}; ++ flow_extract(packet, &flow); ++ if (arp_dup_available() == true && (is_arp(&flow) || is_nd(&flow, NULL) || is_rarp(&flow))) { ++ packet->mbuf.dynfield1[7] = 1; ++ } ++ } + + odp_execute_actions(&aux, packets, should_steal, actions, + actions_len, dp_execute_cb); +@@ -10035,6 +10588,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 + NULL, /* cache_get_supported_levels */ + NULL, /* cache_get_name */ + NULL, /* cache_get_size */ +diff --git a/lib/dpif-netdev.h b/lib/dpif-netdev.h +index 6db6ed2..84e8b6c 100644 +--- a/lib/dpif-netdev.h ++++ b/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/lib/dpif-netlink.c b/lib/dpif-netlink.c +index 84e2bd8..5d97da2 100644 +--- a/lib/dpif-netlink.c ++++ b/lib/dpif-netlink.c +@@ -2296,7 +2296,9 @@ parse_flow_put(struct dpif_netlink *dpif, struct dpif_flow_put *put) + netdev_close(outdev); + } + } +- ++#ifdef HAVE_HWOFF_AGENT ++ info.pmd = NULL; ++#endif + info.recirc_id_shared_with_tc = (dpif->user_features + & OVS_DP_F_TC_RECIRC_SHARING); + err = netdev_flow_put(dev, &match, +@@ -3345,6 +3347,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) +@@ -3357,6 +3373,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, +@@ -4589,6 +4606,9 @@ const struct dpif_class dpif_netlink_class = { + dpif_netlink_cache_get_name, + dpif_netlink_cache_get_size, + dpif_netlink_cache_set_size, ++#ifdef HAVE_HWOFF_AGENT ++ NULL, ++#endif + }; + + static int +diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h +index 520e21e..f0dbbf9 100644 +--- a/lib/dpif-provider.h ++++ b/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" { +@@ -492,6 +493,7 @@ 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). */ ++ + int (*ct_flush)(struct dpif *, const uint16_t *zone, + const struct ct_dpif_tuple *tuple); + /* Set max connections allowed. */ +@@ -683,6 +685,10 @@ struct dpif_class { + + /* Set cache size. */ + int (*cache_set_size)(struct dpif *dpif, uint32_t level, uint32_t size); ++ ++#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/lib/dpif.c b/lib/dpif.c +index d07241f..2018c60 100644 +--- a/lib/dpif.c ++++ b/lib/dpif.c +@@ -603,7 +603,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; +@@ -614,7 +614,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_port); +- } ++ // } + } else { + VLOG_WARN_RL(&error_rl, "%s: failed to add %s as port: %s", + dpif_name(dpif), netdev_name, ovs_strerror(error)); +@@ -1553,6 +1553,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/lib/dpif.h b/lib/dpif.h +index 0f2dc2e..84c9e09 100644 +--- a/lib/dpif.h ++++ b/lib/dpif.h +@@ -604,6 +604,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. */ +@@ -853,6 +856,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/lib/hwoff_init_func.c b/lib/hwoff_init_func.c +new file mode 100644 +index 0000000..3de7f14 +--- /dev/null ++++ b/lib/hwoff_init_func.c +@@ -0,0 +1,153 @@ ++/* ++ * Copyright (c) 2025 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 "mac-learning.h" ++#include ++#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_is_support_offload), ++ ADD_FUNC(hwoff_global_add_vxlan_vtep), ++ ADD_FUNC(hwoff_global_del_vxlan_vtep), ++ ADD_FUNC(hwoff_global_add_geneve_vtep), ++ ADD_FUNC(hwoff_global_del_geneve_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), ++ ADD_FUNC(hwoff_ovs_init), ++ ADD_FUNC(hwoff_ovs_flow_create), ++ ADD_FUNC(hwoff_ovs_flow_query), ++ ADD_FUNC(hwoff_ovs_flow_destroy), ++ ADD_FUNC(hwoff_set_flow_qos), ++ ADD_FUNC(hwoff_del_flow_qos), ++ ADD_FUNC(hwoff_query_flow_qos_stats), ++}; ++ ++hwoff_func* hwoff_get_funcs(void) ++{ ++ return &hwoff_funcs; ++} ++ ++static int hwoff_offload_agent_init(void) ++{ ++ int ret = 0; ++ hwoff_func* func = NULL; ++ func = hwoff_get_funcs(); ++ ++ if (func->hwoff_ovs_init != NULL) { ++ ret = func->hwoff_ovs_init(); ++ if (ret != 0) { ++ return -1; ++ } ++ } ++ ++ hwoff_rarp_mac_list_init(); ++ return ret; ++} ++ ++int hwoff_funcs_init(void) ++{ ++ void *handler = dlopen(HWOFF_SHARED_LIB, RTLD_NOW); ++ int ret = 0; ++ ++ if (handler == NULL) { ++ RTE_LOG(ERR, EAL, "%s load err\n", HWOFF_SHARED_LIB); ++ 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", HWOFF_SHARED_LIB, func_cfgs[index].name); ++ dlclose(handler); ++ return -1; ++ } ++ } ++ ++ ret = hwoff_offload_agent_init(); ++ if (ret != 0) { ++ RTE_LOG(ERR, EAL, "hwoff offload agent init failed"); ++ } ++ return ret; ++} ++ ++int hwoff_key_val_init(struct hwoff_key_val_node **dst,const struct smap *src) ++{ ++ size_t num_pairs = smap_count(src); ++ *dst = xmalloc(num_pairs * sizeof(struct hwoff_key_val_node)); ++ if (*dst == NULL) { ++ RTE_LOG(ERR, EAL, "hwoff key val init xmalloc failed"); ++ return -1; ++ } ++ ++ struct smap_node *node; ++ int i = 0; ++ SMAP_FOR_EACH (node, src) { ++ (*dst)[i].key = xstrdup(node->key); ++ (*dst)[i].value = xstrdup(node->value); ++ if ((*dst)[i].key == NULL ||(*dst)[i].value == NULL) { ++ RTE_LOG(ERR, EAL, "hwoff key val init xstrdup failed"); ++ if((*dst)[i].key != NULL) { ++ free((*dst)[i].key); ++ (*dst)[i].key = NULL; ++ } ++ if((*dst)[i].value != NULL) { ++ free((*dst)[i].value); ++ (*dst)[i].value = NULL; ++ } ++ for (int j = 0; j < i; j++) { ++ free((*dst)[j].key); ++ free((*dst)[j].value); ++ (*dst)[j].key = NULL; ++ (*dst)[j].value = NULL; ++ } ++ free((*dst)); ++ (*dst) = NULL; ++ return -1; ++ } ++ i++; ++ } ++ return 0; ++} ++ ++void hwoff_key_val_destroy(struct hwoff_key_val_node *hwoff_key_val,size_t size) ++{ ++ if (hwoff_key_val == NULL) { ++ return; ++ } ++ for (int i = 0; i < size; i++) { ++ free(hwoff_key_val[i].key); ++ free(hwoff_key_val[i].value); ++ hwoff_key_val[i].key = NULL; ++ hwoff_key_val[i].value = NULL; ++ } ++ free(hwoff_key_val); ++ hwoff_key_val = NULL; ++} +\ No newline at end of file +diff --git a/lib/hwoff_init_func.h b/lib/hwoff_init_func.h +new file mode 100644 +index 0000000..30e3ac5 +--- /dev/null ++++ b/lib/hwoff_init_func.h +@@ -0,0 +1,50 @@ ++/* ++ * Copyright (c) 2025 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" ++#include "smap.h" ++typedef struct { ++ bool (*hwoff_is_ethdev)(const struct netdev *netdev); ++ bool (*hwoff_is_support_offload)(const struct netdev *netdev); ++ 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); ++ int (*hwoff_global_add_geneve_vtep)(bool is_ipv6, uint8_t *geneve_dstip, uint32_t length); ++ int (*hwoff_global_del_geneve_vtep)(bool is_ipv6, uint8_t *geneve_dstip, uint32_t length); ++ uint16_t (*hwoff_tnl_get_src_port)(struct dp_packet *one_pkt); ++ void (*hwoff_parse_ovs_other_config)(const struct hwoff_key_val_node *ovs_other_config, size_t size); ++ void (*hwoff_set_offload_state)(hwoff_offload_state_t offload); ++ void (*hwoff_set_qos)(uint16_t port_id, const char *type, ++ const struct hwoff_key_val_node *details, size_t size); ++ 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 hwoff_key_val_node *details, size_t size); ++ int (*hwoff_ovs_init)(void); ++ struct rte_flow* (*hwoff_ovs_flow_create)(struct rte_eth_dev *dev, struct hwoff_key_val_node *offload_info, uint8_t info_size, ++ struct rte_flow_action *mega_action, struct rte_flow_error *error); ++ int (*hwoff_ovs_flow_query)(struct rte_eth_dev *dev, struct rte_flow *mega_flow, void *data, struct rte_flow_error *error); ++ int (*hwoff_ovs_flow_destroy)(struct rte_eth_dev *dev, struct rte_flow *mega_flow, struct rte_flow_error *error); ++ int (*hwoff_set_flow_qos)(uint32_t mtr_id, struct rte_mtr_meter_profile *profile, uint32_t user_meter_id); ++ int (*hwoff_del_flow_qos)(uint32_t mtr_id); ++ int (*hwoff_query_flow_qos_stats)(uint32_t mtr_id, struct rte_mtr_stats *meter_stat); ++} hwoff_func; ++ ++hwoff_func* hwoff_get_funcs(void); ++int hwoff_funcs_init(void); ++int hwoff_key_val_init(struct hwoff_key_val_node **dst,const struct smap *src); ++void hwoff_key_val_destroy(struct hwoff_key_val_node *hwoff_key_val,size_t size); ++#endif +diff --git a/lib/mac-learning.c b/lib/mac-learning.c +index 5932e27..c4a4071 100644 +--- a/lib/mac-learning.c ++++ b/lib/mac-learning.c +@@ -30,12 +30,97 @@ + #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); + COVERAGE_DEFINE(mac_learning_static_none_move); + ++ ++#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 ++ + /* + * This function will return age of mac entry in the fdb. + * It will return either one of the following: +@@ -517,12 +602,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; +@@ -544,7 +638,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); +@@ -553,6 +659,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; + } + +@@ -562,11 +669,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; +@@ -583,8 +698,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/lib/mac-learning.h b/lib/mac-learning.h +index 270fbd7..374a437 100644 +--- a/lib/mac-learning.h ++++ b/lib/mac-learning.h +@@ -126,6 +126,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); +@@ -223,10 +247,17 @@ struct mac_entry *mac_learning_insert(struct mac_learning *ml, + const struct eth_addr src, + uint16_t vlan) + OVS_REQ_WRLOCK(ml->rwlock); ++#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) ++ int vlan, bool is_gratuitous_arp, bool is_bond, ++ void *in_port) + OVS_EXCLUDED(ml->rwlock); ++#endif + bool mac_learning_add_static_entry(struct mac_learning *ml, + const struct eth_addr src, + uint16_t vlan, void *in_port) +diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c +index 45f6193..b2c4b95 100644 +--- a/lib/netdev-dpdk.c ++++ b/lib/netdev-dpdk.c +@@ -40,6 +40,7 @@ + #include + #include + #include ++#include + + #include "cmap.h" + #include "coverage.h" +@@ -73,6 +74,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}; + +@@ -113,7 +117,7 @@ static bool per_port_memory = false; /* Status of per port memory support */ + * 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 + +@@ -465,6 +469,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; +@@ -770,6 +778,7 @@ dpdk_calculate_mbufs(struct netdev_dpdk *dev, int mtu) + return n_mbufs; + } + ++#define HWOFF_MBUF_SIZE 2176 + static struct dpdk_mp * + dpdk_mp_create(struct netdev_dpdk *dev, int mtu) + { +@@ -794,7 +803,11 @@ dpdk_mp_create(struct netdev_dpdk *dev, int mtu) + 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); + +@@ -1411,8 +1424,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) { +@@ -1515,6 +1533,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); + +@@ -2055,6 +2076,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; ++} ++ ++static 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 identifier) + * is enough for identifying a specific DPDK port. +@@ -2095,6 +2213,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); + } +@@ -2177,7 +2302,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->user_n_rxq) { + dev->user_n_rxq = new_n_rxq; + netdev_request_reconfigure(&dev->up); +@@ -2354,6 +2484,24 @@ netdev_dpdk_set_config(struct netdev *netdev, const struct smap *args, + goto out; + } + ++#ifdef HAVE_HWOFF_AGENT ++ hwoff_func* funcs = hwoff_get_funcs(); ++ ++ size_t num_pairs = smap_count(args); ++ struct hwoff_key_val_node *hwoff_key_val = NULL; ++ err = hwoff_key_val_init(&hwoff_key_val, args); ++ if (err != 0){ ++ goto out; ++ } ++ ++ err = funcs->hwoff_parse_vf_extra_options(dev->port_id, hwoff_key_val, num_pairs); ++ if (err == 0) { ++ dev->hwoff_reconfigure = true; ++ netdev_request_reconfigure(netdev); ++ } ++ hwoff_key_val_destroy(hwoff_key_val, num_pairs); ++#endif ++ + ret = rte_eth_dev_info_get(dev->port_id, &info); + + dpdk_process_queue_size(netdev, args, !ret ? &info : NULL, true); +@@ -3034,7 +3182,7 @@ dpdk_copy_dp_packet_to_mbuf(struct rte_mempool *mp, struct dp_packet *pkt_orig) + mbuf_dest->l3_len = 0; + } + } +- ++ pkt_dest->mbuf.dynfield1[7] = pkt_orig->mbuf.dynfield1[7]; + return pkt_dest; + } + +@@ -3194,6 +3342,11 @@ netdev_dpdk_vhost_send(struct netdev *netdev, int qid, + return 0; + } + ++static inline bool is_rarp(const struct flow *flow) ++{ ++ return (flow->dl_type == htons(ETH_TYPE_RARP)); ++} ++ + static int + netdev_dpdk_eth_send(struct netdev *netdev, int qid, + struct dp_packet_batch *batch, bool concurrent_txq) +@@ -3219,6 +3372,15 @@ netdev_dpdk_eth_send(struct netdev *netdev, int qid, + + cnt = netdev_dpdk_common_send(netdev, batch, &stats); + ++ for (uint32_t i = 0; i < cnt; i++) { ++ struct dp_packet *packet = pkts[i]; ++ struct flow flow; ++ flow_extract(packet, &flow); ++ if (arp_dup_available() == true && (is_arp(&flow) || is_nd(&flow, NULL) || is_rarp(&flow))) { ++ packet->mbuf.dynfield1[7] = 1; ++ } ++ } ++ + dropped = netdev_dpdk_eth_tx_burst(dev, qid, pkts, cnt); + stats.tx_failure_drops += dropped; + dropped += batch_cnt - cnt; +@@ -4031,8 +4193,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); + +@@ -5156,8 +5339,38 @@ 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 ++ size_t num_pairs = smap_count(details); ++ struct hwoff_key_val_node *hwoff_key_val = NULL; ++ error = hwoff_key_val_init(&hwoff_key_val, details); ++ if (error != 0){ ++ return error; ++ } ++ ++ if (eth_flag) { ++ if (funcs->hwoff_set_qos) { ++ funcs->hwoff_set_qos(dev->port_id, type, hwoff_key_val, num_pairs); ++ } ++ hwoff_key_val_destroy(hwoff_key_val, num_pairs); ++ ovs_mutex_unlock(&dev->mutex); ++ return error; ++ } ++ hwoff_key_val_destroy(hwoff_key_val, num_pairs); ++#endif ++ + qos_conf = ovsrcu_get_protected(struct qos_conf *, &dev->qos_conf); + + new_ops = qos_lookup_name(type); +@@ -5985,7 +6198,12 @@ netdev_dpdk_reconfigure(struct netdev *netdev) + && dev->txq_size == dev->requested_txq_size + && eth_addr_equals(dev->hwaddr, dev->requested_hwaddr) + && 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; +@@ -6268,9 +6486,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; + } +@@ -6292,6 +6508,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) { +diff --git a/lib/netdev-dpdk.h b/lib/netdev-dpdk.h +index 86df7a1..683ab1f 100644 +--- a/lib/netdev-dpdk.h ++++ b/lib/netdev-dpdk.h +@@ -22,6 +22,10 @@ + #include "openvswitch/compiler.h" + #include "smap.h" + ++#ifdef HAVE_HWOFF_AGENT ++#include ++#endif ++ + struct dp_packet; + struct netdev; + +@@ -88,6 +92,14 @@ int netdev_dpdk_rte_flow_tunnel_item_release(struct netdev *, + uint32_t num_of_items, + struct rte_flow_error *); + ++#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 int +diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c +index dee9ab3..7bfd99e 100644 +--- a/lib/netdev-native-tnl.c ++++ b/lib/netdev-native-tnl.c +@@ -45,6 +45,9 @@ + #include "unixctl.h" + #include "util.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); +@@ -323,7 +326,16 @@ netdev_tnl_push_udp_header(const struct netdev *netdev OVS_UNUSED, + &ip_tot_size, 0); + + /* 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/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c +index 623005b..1c33f9a 100644 +--- a/lib/netdev-offload-dpdk.c ++++ b/lib/netdev-offload-dpdk.c +@@ -34,6 +34,12 @@ + #include "ovs-rcu.h" + #include "packets.h" + #include "uuid.h" ++#ifdef HAVE_HWOFF_AGENT ++#include "odp-util.h" ++#include "unixctl.h" ++#include "hwoff_init_func.h" ++#endif ++#include "ovs-thread.h" + + VLOG_DEFINE_THIS_MODULE(netdev_offload_dpdk); + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(600, 600); +@@ -61,6 +67,10 @@ struct ufid_to_rte_flow_data { + ovs_u128 ufid; + struct netdev *netdev; + struct rte_flow *rte_flow; ++#ifdef HAVE_HWOFF_AGENT ++ struct ovs_rwlock flow_mutex; ++#endif ++ int ref_cnt; + bool actions_offloaded; + struct dpif_flow_stats stats; + struct netdev *physdev; +@@ -1784,6 +1794,7 @@ add_represented_port_action(struct flow_actions *actions, + int outdev_id; + + outdev_id = netdev_dpdk_get_port_id(outdev); ++ + if (outdev_id < 0) { + return -1; + } +@@ -1799,14 +1810,19 @@ add_represented_port_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; +@@ -1817,7 +1833,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; + } + +@@ -1845,10 +1864,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; + } + +@@ -1877,6 +1898,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, +@@ -1960,11 +2079,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, +@@ -2060,6 +2180,9 @@ parse_vlan_push_action(struct flow_actions *actions, + + rte_vlan_pcp = xzalloc(sizeof *rte_vlan_pcp); + rte_vlan_pcp->vlan_pcp = vlan_tci_to_pcp(vlan_push->vlan_tci); ++#ifdef HAVE_HWOFF_AGENT ++ rte_vlan_pcp->vlan_pcp = rte_vlan_pcp->vlan_pcp << 5; ++#endif + add_flow_action(actions, RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_PCP, + rte_vlan_pcp); + +@@ -2093,7 +2216,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; +@@ -2105,9 +2229,13 @@ parse_clone_actions(struct netdev *netdev, + const struct ovs_action_push_tnl *tnl_push = nl_attr_get(ca); + add_tunnel_push_action(actions, tnl_push); + } 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); ++#endif + } else { + VLOG_DBG_RL(&rl, + "Unsupported nested action inside clone(), " +@@ -2175,7 +2303,8 @@ 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) + { + struct nlattr *nla; + size_t left; +@@ -2183,8 +2312,13 @@ parse_flow_actions(struct netdev *netdev, + 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 if (nl_attr_type(nla) == OVS_ACTION_ATTR_DROP) { + add_flow_action(actions, RTE_FLOW_ACTION_TYPE_DROP, NULL); +@@ -2210,17 +2344,44 @@ parse_flow_actions(struct netdev *netdev, + const struct ovs_action_push_tnl *tnl_push = nl_attr_get(nla); + + add_tunnel_push_action(actions, tnl_push); ++#ifdef HAVE_HWOFF_AGENT ++ } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_METER) { ++ struct rte_flow_action_meter *mtr = xzalloc(sizeof *mtr); ++ mtr->mtr_id = nl_attr_get_u32(nla); ++ add_flow_action(actions, RTE_FLOW_ACTION_TYPE_METER, mtr); ++ } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_RECIRC) { ++ } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_CT) { ++ } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_CLONE) { ++#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; + } +-#ifdef ALLOW_EXPERIMENTAL_API /* Packet restoration API required. */ + } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_TUNNEL_POP) { ++#ifdef HAVE_HWOFF_AGENT ++ 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); ++ } ++ if (!strcmp(netdev_get_type(vport), "geneve")) { ++ /* if exists tunnel_pop action, it should be the first action */ ++ free_flow_actions(actions); ++ add_flow_action(actions, RTE_FLOW_ACTION_TYPE_RAW_DECAP, NULL); ++ } ++ netdev_close(vport); ++#elif defined (ALLOW_EXPERIMENTAL_API) + if (add_tnl_pop_action(netdev, actions, nla)) { + return -1; + } +@@ -2256,7 +2417,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; + } +@@ -2774,3 +2935,505 @@ const struct netdev_flow_api netdev_offload_dpdk = { + .hw_miss_packet_recover = netdev_offload_dpdk_hw_miss_packet_recover, + .flow_get_n_flows = netdev_offload_dpdk_get_n_flows, + }; ++ ++#ifdef HAVE_HWOFF_AGENT ++#define HWOFF_OFFLOAD_INFO_NUM 10 ++#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) ++{ ++ free(flow_data); ++} ++ ++static struct ufid_to_rte_flow_data* hiovs_rte_flow_data_alloc(const ovs_u128 *ufid) ++{ ++ 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)); ++ ++ flow_data->ufid = *ufid; ++ flow_data->actions_offloaded = false; ++ flow_data->ref_cnt = 1; ++ ovs_rwlock_init(&flow_data->flow_mutex); ++ 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(); ++} ++ ++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; ++} ++ ++static void hiovs_offload_info_parse(struct hwoff_key_val_node *offload_node, ++ struct offload_info *info, ++ const ovs_u128 *sw_ufid) ++{ ++ offload_node[0].type = HWOFF_OVS_INPUT_KEY_PMD_IDX; ++ offload_node[0].value = (char*)&info->pmd_core_id; ++ ++ offload_node[1].type = HWOFF_OVS_INPUT_KEY_PORT_ID; ++ offload_node[1].value = (char*)&info->in_port_id; ++ ++ offload_node[2].type = HWOFF_OVS_INPUT_KEY_PORT_TYPE; ++ offload_node[2].value = (char*)&info->in_port_type; ++ ++ offload_node[3].type = HWOFF_OVS_INPUT_KET_PACKET_BACTH; ++ offload_node[3].value = (char*)info->pkts_info; ++ ++ offload_node[4].type = HWOFF_OVS_INPUT_KEY_MEGA_UFID; ++ offload_node[4].value = (char *)sw_ufid; ++ 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 = 0; ++ bool flag = false; ++ hwoff_func* funcs = hwoff_get_funcs(); ++ ++ flag = funcs->hwoff_is_support_offload(netdev); ++ if (flag == true) { ++ ret = funcs->hwoff_ovs_flow_query(netdev, rte_flow, query, error); ++ return ret; ++ } ++ return ret; ++} ++ ++static int hiovs_offload_flow_del_exec(struct netdev *netdev, struct rte_flow *rte_flow, struct rte_flow_error *error) ++{ ++ if (rte_flow == NULL) { ++ return 0; ++ } ++ ++ int ret = 0; ++ bool flag = false; ++ hwoff_func* funcs = hwoff_get_funcs(); ++ flag = funcs->hwoff_is_support_offload(netdev); ++ if (flag == true) { ++ ret = funcs->hwoff_ovs_flow_destroy(netdev, rte_flow, error); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static struct rte_flow* hiovs_offload_flow_add_exec(struct netdev *netdev, ++ struct hwoff_key_val_node *offload_info, ++ uint8_t info_size, ++ struct rte_flow_action *mega_action, ++ struct rte_flow_error *error) ++{ ++ bool flag = false; ++ struct rte_flow *flow = NULL; ++ hwoff_func* funcs = hwoff_get_funcs(); ++ ++ flag = funcs->hwoff_is_support_offload(netdev); ++ if (flag == true) { ++ flow = funcs->hwoff_ovs_flow_create(netdev, offload_info, info_size, mega_action, error); ++ return flow; ++ } ++ ++ return flow; ++} ++ ++static void hwoff_offload_packet_is_recirc(struct flow_actions *actions, bool *is_recirc, bool *is_ct, ++ struct nlattr *nl_actions, size_t nl_actions_len) ++{ ++ 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_CT) { ++ *is_ct = true; ++ continue; ++ } ++ ++ if (nl_attr_type(nla) == OVS_ACTION_ATTR_RECIRC) { ++ *is_recirc = true; ++ continue; ++ } ++ ++ if (nl_attr_type(nla) == OVS_ACTION_ATTR_CLONE) { ++ const struct nlattr *clone_actions = nl_attr_get(nla); ++ size_t clone_actions_len = nl_attr_get_size(nla); ++ const struct nlattr *ca; ++ unsigned int cleft; ++ NL_ATTR_FOR_EACH_UNSAFE (ca, cleft, clone_actions, clone_actions_len) { ++ int clone_type = nl_attr_type(ca); ++ if (clone_type == OVS_ACTION_ATTR_CT) { ++ *is_ct = true; ++ continue; ++ } ++ if (clone_type == OVS_ACTION_ATTR_RECIRC) { ++ *is_recirc = true; ++ continue; ++ } ++ } ++ } ++ } ++ ++ for (int i = 0; i < actions->cnt; ++i) { ++ if (actions->actions[i].type == RTE_FLOW_ACTION_TYPE_VXLAN_DECAP) { ++ *is_recirc = true; ++ return; ++ } ++ ++ if (actions->actions[i].type == RTE_FLOW_ACTION_TYPE_RAW_DECAP) { ++ *is_recirc = true; ++ return; ++ } ++ ++ if (actions->actions[i].type == RTE_FLOW_ACTION_TYPE_END) { ++ break; ++ } ++ } ++ ++ return; ++} ++ ++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; ++ struct flow_actions actions = { .actions = NULL, .cnt = 0 }; ++ struct flow_patterns patterns = { .items = NULL, .cnt = 0 }; ++ ++ struct hwoff_key_val_node hwoff_offload_info[HWOFF_OFFLOAD_INFO_NUM] = {0}; ++ bool is_recirc = false; ++ bool is_ct = false; ++ uint32_t table_id = 1; ++ ++ hiovs_offload_info_parse(hwoff_offload_info, info, &flow_data->ufid); ++ ret = parse_flow_actions(netdev, &actions, nl_actions, actions_len, info->pmd); ++ if (ret != 0) { ++ goto out; ++ } ++ ++ hwoff_offload_packet_is_recirc(&actions, &is_recirc, &is_ct, nl_actions, actions_len); ++ if (is_recirc) { ++ hwoff_offload_info[5].type = HWOFF_OVS_INPUT_KEY_NEED_REIRC; ++ } ++ if (is_ct) { ++ hwoff_offload_info[6].type = HWOFF_OVS_INPUT_KEY_IS_CT; ++ } ++ ++ ovs_rwlock_rdlock(&flow_data->flow_mutex); ++ if (flow_data->rte_flow != NULL) { ++ hwoff_offload_info[7].type = HWOFF_OVS_MEGA_FLOW; ++ hwoff_offload_info[7].value = (char *)flow_data->rte_flow; ++ } ++ ++ hwoff_offload_info[8].type = HWOFF_OVS_TABLE_ID; ++ hwoff_offload_info[8].value = (char*)&table_id; ++ ++ memset(&error, 0, sizeof(error)); ++ flow = hiovs_offload_flow_add_exec(netdev, hwoff_offload_info, HWOFF_OFFLOAD_INFO_NUM, actions.actions, &error); ++ if (flow == NULL) { ++ ovs_rwlock_unlock(&flow_data->flow_mutex); ++ ret = -1; ++ goto out; ++ } ++ ++ if (flow_data->rte_flow == NULL) { ++ flow_data->rte_flow = flow; ++ } ++ ovs_rwlock_unlock(&flow_data->flow_mutex); ++ ++ 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; ++ 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; ++ } ++ ++ ovs_rwlock_wrlock(&flow_data->flow_mutex); ++ ret = hiovs_offload_flow_del_exec(netdev, flow_data->rte_flow, &error); ++ if (ret != 0) { ++ ovs_rwlock_unlock(&flow_data->flow_mutex); ++ hiovs_rte_flow_map_unlock(); ++ return ret; ++ } ++ flow_data->rte_flow = NULL; ++ ovs_rwlock_unlock(&flow_data->flow_mutex); ++ ++ hash = hash_bytes(&flow_data->ufid, sizeof(ovs_u128), 0); ++ ++ 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; ++ 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; ++ } ++ ++ if (info->in_port_type == HWOFF_PORT_TYPE_HIOVS) { ++ info->in_port_id = netdev_dpdk_get_port_id(netdev); ++ } else { ++ info->in_port_id = 0; ++ } ++ ++ 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"; ++ if (flow_data->rte_flow != NULL) { ++ 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, "geneve") == 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; ++ 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, "ref_cnt=%d\n", 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 +\ No newline at end of file +diff --git a/lib/netdev-offload-provider.h b/lib/netdev-offload-provider.h +index 9108856..560afb4 100644 +--- a/lib/netdev-offload-provider.h ++++ b/lib/netdev-offload-provider.h +@@ -141,6 +141,11 @@ 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); ++#endif ++ + #ifdef __cplusplus + } + #endif +diff --git a/lib/netdev-offload.c b/lib/netdev-offload.c +index 931d634..b76aace 100644 +--- a/lib/netdev-offload.c ++++ b/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" +@@ -181,6 +186,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); + atomic_store_relaxed(&netdev->hw_info.miss_api_supported, true); +@@ -902,3 +913,43 @@ netdev_set_flow_api_enabled(const struct smap *ovs_other_config) + } + } + } ++ ++#ifdef HAVE_HWOFF_AGENT ++static bool arp_dup_enable = true; ++ ++bool arp_dup_available(void) ++{ ++ return arp_dup_enable; ++} ++ ++int ++netdev_offload_hw_init(const struct smap *ovs_other_config) ++{ ++ int ret; ++ static bool hwoff_agent_init = false; ++ ++ if (smap_get_bool(ovs_other_config, "arp-dup", false)) { ++ arp_dup_enable = true; ++ } else { ++ arp_dup_enable = 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/lib/netdev-offload.h b/lib/netdev-offload.h +index 47f8e6f..3a15456 100644 +--- a/lib/netdev-offload.h ++++ b/lib/netdev-offload.h +@@ -26,13 +26,18 @@ + #include "openvswitch/ofp-meter.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; +@@ -76,7 +81,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. */ + odp_port_t orig_in_port; /* Originating in_port for tnl flows. */ +@@ -162,6 +175,10 @@ void meter_offload_set(ofproto_meter_id, struct ofputil_meter_config *); + int meter_offload_get(ofproto_meter_id, struct ofputil_meter_stats *); + int meter_offload_del(ofproto_meter_id, struct ofputil_meter_stats *); + ++#ifdef HAVE_HWOFF_AGENT ++int netdev_offload_hw_init(const struct smap *ovs_other_config); ++#endif ++ + #ifdef __cplusplus + } + #endif +diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c +index 60caa02..d5ce4af 100644 +--- a/lib/netdev-vport.c ++++ b/lib/netdev-vport.c +@@ -52,6 +52,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); + +@@ -72,6 +76,12 @@ static void update_vxlan_global_cfg(struct netdev *, + const struct netdev_tunnel_config *, + const struct netdev_tunnel_config *); + ++#ifdef HAVE_HWOFF_AGENT ++static void update_geneve_global_cfg(struct netdev *, ++ const struct netdev_tunnel_config *, ++ const struct netdev_tunnel_config *); ++#endif ++ + struct vport_class { + const char *dpif_port; + struct netdev_class netdev_class; +@@ -221,6 +231,9 @@ netdev_vport_construct(struct netdev *netdev_) + * destination port */ + if (!strcmp(type, "geneve")) { + tnl_cfg->dst_port = port ? htons(port) : htons(GENEVE_DST_PORT); ++#ifdef HAVE_HWOFF_AGENT ++ update_geneve_global_cfg(netdev_, NULL, tnl_cfg); ++#endif + } else if (!strcmp(type, "vxlan")) { + tnl_cfg->dst_port = port ? htons(port) : htons(VXLAN_DST_PORT); + update_vxlan_global_cfg(netdev_, NULL, tnl_cfg); +@@ -252,6 +265,11 @@ netdev_vport_destruct(struct netdev *netdev_) + if (!strcmp(type, "vxlan")) { + update_vxlan_global_cfg(netdev_, tnl_cfg, NULL); + } ++#ifdef HAVE_HWOFF_AGENT ++ if (!strcmp(type, "geneve")) { ++ update_geneve_global_cfg(netdev_, tnl_cfg, NULL); ++ } ++#endif + + ovsrcu_set(&netdev->tnl_cfg, NULL); + ovsrcu_postpone(free, CONST_CAST(struct netdev_tunnel_config *, tnl_cfg)); +@@ -511,6 +529,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, + const struct netdev_tunnel_config *old_cfg, +@@ -521,6 +553,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((struct in6_addr *)&new_cfg->ipv6_src); ++ funcs->hwoff_global_add_vxlan_vtep(is_ipv6, (uint8_t *)&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 && +@@ -540,6 +583,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((struct in6_addr *)&old_cfg->ipv6_src); ++ funcs->hwoff_global_del_vxlan_vtep(is_ipv6, (uint8_t *)&old_cfg->ipv6_src, sizeof(struct in6_addr)); ++ } ++#endif + } + } + } +@@ -554,6 +603,56 @@ update_vxlan_global_cfg(struct netdev *netdev, + } + } + ++#ifdef HAVE_HWOFF_AGENT ++static void ++update_geneve_global_cfg(struct netdev *netdev, ++ const struct netdev_tunnel_config *old_cfg, ++ const struct netdev_tunnel_config *new_cfg) ++{ ++ unsigned int count; ++ char namebuf[20]; ++ const char *type = netdev_get_type(netdev); ++ struct vport_class *vclass = vport_class_cast(netdev_get_class(netdev)); ++ ++ ++ hwoff_func* funcs = hwoff_get_funcs(); ++ bool is_ipv6 = false; ++ if (strcmp(type, "geneve") == 0 && new_cfg && ++ funcs->hwoff_global_add_geneve_vtep && ++ (old_cfg == NULL || memcmp(&new_cfg->ipv6_src, &old_cfg->ipv6_src, sizeof(struct in6_addr)))) { ++ is_ipv6 = hwoff_is_ipv6_addr((struct in6_addr *)&new_cfg->ipv6_src); ++ funcs->hwoff_global_add_geneve_vtep(is_ipv6, (uint8_t *)&new_cfg->ipv6_src, sizeof(struct in6_addr)); ++ } ++ ++ if (strcmp(type, "geneve") || ++ (old_cfg != NULL && new_cfg != NULL && ++ old_cfg->dst_port == new_cfg->dst_port)) { ++ return; ++ } ++ ++ if (old_cfg != NULL) { ++ snprintf(namebuf, sizeof(namebuf), "dst_port_%d", old_cfg->dst_port); ++ count = simap_get(&vclass->global_cfg_tracker, namebuf); ++ if (count != 0) { ++ if (--count) { ++ simap_put(&vclass->global_cfg_tracker, namebuf, count); ++ } else { ++ simap_find_and_delete(&vclass->global_cfg_tracker, namebuf); ++ if (funcs->hwoff_global_del_geneve_vtep) { ++ is_ipv6 = hwoff_is_ipv6_addr((struct in6_addr *)&old_cfg->ipv6_src); ++ funcs->hwoff_global_del_geneve_vtep(is_ipv6, (uint8_t *)&old_cfg->ipv6_src, sizeof(struct in6_addr)); ++ } ++ } ++ } ++ } ++ ++ if (new_cfg != NULL) { ++ snprintf(namebuf, sizeof(namebuf), "dst_port_%d", new_cfg->dst_port); ++ simap_increase(&vclass->global_cfg_tracker, namebuf, 1); ++ } ++} ++#endif ++ + static bool + is_concomitant_vxlan_tunnel_present(struct netdev_vport *dev, + const struct netdev_tunnel_config *tnl_cfg) +@@ -932,6 +1031,9 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp) + + curr_tnl_cfg = vport_tunnel_config(dev); + update_vxlan_global_cfg(dev_, curr_tnl_cfg, &tnl_cfg); ++#ifdef HAVE_HWOFF_AGENT ++ update_geneve_global_cfg(dev_, curr_tnl_cfg, &tnl_cfg); ++#endif + + if (memcmp(curr_tnl_cfg, &tnl_cfg, sizeof tnl_cfg)) { + ovsrcu_set(&dev->tnl_cfg, xmemdup(&tnl_cfg, sizeof tnl_cfg)); +diff --git a/lib/odp-util.c b/lib/odp-util.c +index 9306c9b..baa2879 100644 +--- a/lib/odp-util.c ++++ b/lib/odp-util.c +@@ -4612,6 +4612,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/lib/odp-util.h b/lib/odp-util.h +index 8c7baa6..3b3cb04 100644 +--- a/lib/odp-util.h ++++ b/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/lib/ofp-meter.c b/lib/ofp-meter.c +index 9ea40a0..7fee916 100644 +--- a/lib/ofp-meter.c ++++ b/lib/ofp-meter.c +@@ -261,6 +261,10 @@ ofputil_append_meter_stats(struct ovs_list *replies, + reply->byte_in_count = htonll(ms->byte_in_count); + reply->duration_sec = htonl(ms->duration_sec); + reply->duration_nsec = htonl(ms->duration_nsec); ++#ifdef HAVE_HWOFF_AGENT ++ reply->n_pkts_dropped = ntohll(ms->n_num_dropped_pkts); ++ reply->n_bytes_dropped = ntohll(ms->n_num_dropped_byte); ++#endif + + for (n = 0; n < ms->n_bands; ++n) { + const struct ofputil_meter_band_stats *src = &ms->bands[n]; +@@ -433,6 +437,10 @@ ofputil_decode_meter_stats(struct ofpbuf *msg, + ms->byte_in_count = ntohll(oms->byte_in_count); + ms->duration_sec = ntohl(oms->duration_sec); + ms->duration_nsec = ntohl(oms->duration_nsec); ++#ifdef HAVE_HWOFF_AGENT ++ ms->n_num_dropped_byte = ntohll(oms->n_bytes_dropped); ++ ms->n_num_dropped_pkts = ntohll(oms->n_pkts_dropped); ++#endif + ms->bands = bands->data; + + return 0; +@@ -458,6 +466,10 @@ ofputil_format_meter_stats(struct ds *s, const struct ofputil_meter_stats *ms) + ds_put_format(s, "packet_count:%"PRIu64" ", ms->bands[i].packet_count); + ds_put_format(s, "byte_count:%"PRIu64"\n", ms->bands[i].byte_count); + } ++#ifdef HAVE_HWOFF_AGENT ++ ds_put_format(s, "offload_n_pkts_dropped:%"PRIu64" ", ms->n_num_dropped_pkts); ++ ds_put_format(s, "offload_n_bytes_dropped:%"PRIu64"\n", ms->n_num_dropped_byte); ++#endif + } + + void +@@ -631,6 +643,10 @@ parse_ofp_meter_mod_str__(struct ofputil_meter_mod *mm, char *string, + mm->meter.flags |= OFPMF13_BURST; + } else if (fields & F_FLAGS && !strcmp(name, "stats")) { + mm->meter.flags |= OFPMF13_STATS; ++#ifdef HAVE_HWOFF_AGENT ++ } else if (fields & F_FLAGS && !strcmp(name, "offload")) { ++ mm->meter.flags |= OFPMF13_OFFLOAD; ++#endif + } else { + char *value; + +diff --git a/lib/packets.h b/lib/packets.h +index 8b69948..d49cd0b 100644 +--- a/lib/packets.h ++++ b/lib/packets.h +@@ -1494,6 +1494,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/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c +index b5cbeed..15ec74a 100644 +--- a/ofproto/ofproto-dpif-upcall.c ++++ b/ofproto/ofproto-dpif-upcall.c +@@ -57,8 +57,10 @@ COVERAGE_DEFINE(dumped_inconsistent_flow); + COVERAGE_DEFINE(dumped_new_flow); + COVERAGE_DEFINE(handler_duplicate_upcall); + COVERAGE_DEFINE(revalidate_missed_dp_flow); ++COVERAGE_DEFINE(revalidate_missing_dp_flow); + COVERAGE_DEFINE(ukey_dp_change); + COVERAGE_DEFINE(ukey_invalid_stat_reset); ++COVERAGE_DEFINE(ukey_replace_contention); + COVERAGE_DEFINE(upcall_flow_limit_grew); + COVERAGE_DEFINE(upcall_flow_limit_hit); + COVERAGE_DEFINE(upcall_flow_limit_kill); +@@ -301,6 +303,7 @@ struct udpif_key { + uint64_t dump_seq OVS_GUARDED; /* Tracks udpif->dump_seq. */ + uint64_t reval_seq OVS_GUARDED; /* Tracks udpif->reval_seq. */ + enum ukey_state state OVS_GUARDED; /* Tracks ukey lifetime. */ ++ uint32_t missed_dumps OVS_GUARDED; /* Missed consecutive dumps. */ + + /* 'state' debug information. */ + unsigned int state_thread OVS_GUARDED; /* Thread that transitions. */ +@@ -425,6 +428,10 @@ 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 = true; + static atomic_bool enable_ufid = true; + +@@ -483,6 +490,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; + } + +@@ -509,6 +519,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++) { + struct udpif_key *ukey; + +@@ -1051,7 +1064,7 @@ udpif_revalidator(void *arg) + flow_limit += 1000; + COVERAGE_INC(upcall_flow_limit_grew); + } +- 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) { +@@ -1428,8 +1441,6 @@ upcall_cb(const struct dp_packet *packet, const struct flow *flow, ovs_u128 *ufi + } + + if (upcall.ukey && !ukey_install(udpif, upcall.ukey)) { +- static struct vlog_rate_limit rll = VLOG_RATE_LIMIT_INIT(1, 1); +- VLOG_WARN_RL(&rll, "upcall_cb failure: ukey installation fails"); + error = ENOSPC; + } + out: +@@ -1797,6 +1808,7 @@ ukey_create__(const struct nlattr *key, size_t key_len, + ukey->state_thread = ovsthread_id_self(); + ukey->state_where = OVS_SOURCE_LOCATOR; + ukey->created = ukey->flow_time = time_msec(); ++ ukey->missed_dumps = 0; + memset(&ukey->stats, 0, sizeof ukey->stats); + ukey->stats.used = used; + ukey->dp_layer = NULL; +@@ -1927,15 +1939,15 @@ try_ukey_replace(struct umap *umap, struct udpif_key *old_ukey, + transition_ukey(old_ukey, UKEY_DELETED); + transition_ukey(new_ukey, UKEY_VISIBLE); + replaced = true; ++ COVERAGE_INC(upcall_ukey_replace); ++ } else { ++ COVERAGE_INC(handler_duplicate_upcall); + } + ovs_mutex_unlock(&old_ukey->mutex); +- } +- +- if (replaced) { +- COVERAGE_INC(upcall_ukey_replace); + } else { +- COVERAGE_INC(handler_duplicate_upcall); ++ COVERAGE_INC(ukey_replace_contention); + } ++ + return replaced; + } + +@@ -2973,6 +2985,7 @@ revalidator_sweep__(struct revalidator *revalidator, bool purge) + /* Handler threads could be holding a ukey lock while it installs a + * new flow, so don't hang around waiting for access to it. */ + if (ovs_mutex_trylock(&ukey->mutex)) { ++ COVERAGE_INC(upcall_ukey_contention); + continue; + } + ukey_state = ukey->state; +@@ -2995,6 +3008,20 @@ revalidator_sweep__(struct revalidator *revalidator, bool purge) + result = revalidate_ukey(udpif, ukey, &stats, &odp_actions, + reval_seq, &recircs); + } ++ ++ if (ukey->dump_seq != dump_seq) { ++ ukey->missed_dumps++; ++ if (ukey->missed_dumps >= 16) { ++ /* If the flow was not dumped for 4 revalidator rounds, ++ * we can assume the datapath flow no longer exists ++ * and the ukey should be deleted. */ ++ COVERAGE_INC(revalidate_missing_dp_flow); ++ result = UKEY_DELETE; ++ } ++ } else { ++ ukey->missed_dumps = 0; ++ } ++ + if (result != UKEY_KEEP) { + /* Clears 'recircs' if filled by revalidate_ukey(). */ + reval_op_init(&ops[n_ops++], result, udpif, ukey, &recircs, +@@ -3075,7 +3102,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/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c +index 1cf4d5f..7390fa5 100644 +--- a/ofproto/ofproto-dpif-xlate.c ++++ b/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" +@@ -88,6 +89,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) +@@ -2765,13 +2770,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, +@@ -2784,6 +2832,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. */ +@@ -3193,12 +3242,21 @@ xlate_normal(struct xlate_ctx *ctx) + + /* 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 && 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/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c +index f59d69c..008f980 100644 +--- a/ofproto/ofproto-dpif.c ++++ b/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 *); +diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h +index 92d33aa..00043a1 100644 +--- a/ofproto/ofproto-dpif.h ++++ b/ofproto/ofproto-dpif.h +@@ -361,6 +361,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/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h +index 83c509f..bee57fa 100644 +--- a/ofproto/ofproto-provider.h ++++ b/ofproto/ofproto-provider.h +@@ -59,6 +59,7 @@ + #include "tun-metadata.h" + #include "versions.h" + #include "vl-mff-map.h" ++#include "conntrack.h" + + struct match; + struct ofputil_flow_mod; +diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c +index 95a65fc..07910d1 100644 +--- a/vswitchd/bridge.c ++++ b/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" +@@ -557,6 +561,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 +@@ -3288,6 +3297,9 @@ bridge_run__(void) + } + } + ++extern void rte_adapter_init(void); ++extern void ovs_adapter_init(void); ++ + void + bridge_run(void) + { +@@ -3328,6 +3340,23 @@ 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(); ++ ++ size_t num_pairs = smap_count(&cfg->other_config); ++ struct hwoff_key_val_node *hwoff_key_val = NULL; ++ ret = hwoff_key_val_init(&hwoff_key_val, &cfg->other_config); ++ if (ret == 0) { ++ if (funcs->hwoff_parse_ovs_other_config) { ++ funcs->hwoff_parse_ovs_other_config(hwoff_key_val, num_pairs); ++ } ++ } ++ hwoff_key_val_destroy(hwoff_key_val, num_pairs); ++ } ++#endif + } + + /* Initialize the ofproto library. This only needs to run once, but -- Gitee