diff --git a/tests/automake.mk b/tests/automake.mk
index fd5ee14af..eea0d00f4 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -2,11 +2,13 @@ EXTRA_DIST += \
 	$(COMMON_MACROS_AT) \
 	$(TESTSUITE_AT) \
 	$(SYSTEM_TESTSUITE_AT) \
+	$(SYSTEM_DPDK_TESTSUITE_AT) \
 	$(SYSTEM_KMOD_TESTSUITE_AT) \
 	$(SYSTEM_USERSPACE_TESTSUITE_AT) \
 	$(PERF_TESTSUITE_AT) \
 	$(MULTINODE_TESTSUITE_AT) \
 	$(TESTSUITE) \
+	$(SYSTEM_DPDK_TESTSUITE) \
 	$(SYSTEM_KMOD_TESTSUITE) \
 	$(SYSTEM_USERSPACE_TESTSUITE) \
 	$(PERF_TESTSUITE) \
@@ -44,20 +46,22 @@ TESTSUITE_AT = \
 	tests/ovn-ipsec.at \
 	tests/ovn-vif-plug.at
 
+SYSTEM_DPDK_TESTSUITE_AT = \
+	tests/system-dpdk-testsuite.at \
+	tests/system-dpdk-macros.at
+
 SYSTEM_KMOD_TESTSUITE_AT = \
-	tests/system-common-macros.at \
+	tests/system-kmod-macros.at \
 	tests/system-kmod-testsuite.at \
-	tests/system-kmod-macros.at
+	tests/system-ovn-kmod.at
 
 SYSTEM_USERSPACE_TESTSUITE_AT = \
 	tests/system-userspace-testsuite.at \
-	tests/system-ovn.at \
 	tests/system-userspace-macros.at
 
 SYSTEM_TESTSUITE_AT = \
 	tests/system-common-macros.at \
-	tests/system-ovn.at \
-	tests/system-ovn-kmod.at
+	tests/system-ovn.at
 
 PERF_TESTSUITE_AT = \
 	tests/perf-testsuite.at \
@@ -73,6 +77,7 @@ check_SCRIPTS += tests/atlocal
 TESTSUITE = $(srcdir)/tests/testsuite
 TESTSUITE_PATCH = $(srcdir)/tests/testsuite.patch
 TESTSUITE_DIR = $(abs_top_builddir)/tests/testsuite.dir
+SYSTEM_DPDK_TESTSUITE = $(srcdir)/tests/system-dpdk-testsuite
 SYSTEM_KMOD_TESTSUITE = $(srcdir)/tests/system-kmod-testsuite
 SYSTEM_USERSPACE_TESTSUITE = $(srcdir)/tests/system-userspace-testsuite
 PERF_TESTSUITE = $(srcdir)/tests/perf-testsuite
@@ -180,6 +185,10 @@ check-userspace-valgrind: all $(valgrind_wrappers) $(check_DATA)
 check-helgrind: all $(valgrind_wrappers) $(check_DATA)
 	-$(SHELL) '$(TESTSUITE)' -C tests CHECK_VALGRIND=true VALGRIND='$(HELGRIND)' AUTOTEST_PATH='tests/valgrind:$(AUTOTEST_PATH)' -d $(TESTSUITEFLAGS)
 
+check-system-dpdk: all
+	set $(SHELL) '$(SYSTEM_DPDK_TESTSUITE)' -C tests  AUTOTEST_PATH='$(AUTOTEST_PATH)'; \
+	$(SUDO) "$$@" $(TESTSUITEFLAGS) -j1 || (test X'$(RECHECK)' = Xyes && $(SUDO) "$$@" --recheck)
+
 # Run kmod tests. Assume kernel modules has been installed or linked into the kernel
 check-kernel: all
 	set $(SHELL) '$(SYSTEM_KMOD_TESTSUITE)' -C tests  AUTOTEST_PATH='$(AUTOTEST_PATH)'; \
@@ -232,6 +241,10 @@ $(TESTSUITE): package.m4 $(TESTSUITE_AT) $(COMMON_MACROS_AT)
 	$(AM_V_at)mv $@.tmp $@
 endif
 
+$(SYSTEM_DPDK_TESTSUITE): package.m4 $(SYSTEM_TESTSUITE_AT) $(SYSTEM_DPDK_TESTSUITE_AT) $(COMMON_MACROS_AT)
+	$(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
+	$(AM_V_at)mv $@.tmp $@
+
 $(SYSTEM_KMOD_TESTSUITE): package.m4 $(SYSTEM_TESTSUITE_AT) $(SYSTEM_KMOD_TESTSUITE_AT) $(COMMON_MACROS_AT)
 	$(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
 	$(AM_V_at)mv $@.tmp $@
diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at
index 2e0bbd20b..f4ebdafc7 100644
--- a/tests/ofproto-macros.at
+++ b/tests/ofproto-macros.at
@@ -218,11 +218,12 @@ m4_define([_OVS_VSWITCHD_START],
 /ofproto|INFO|using datapath ID/d
 /netdev_linux|INFO|.*device has unknown hardware address family/d
 /ofproto|INFO|datapath ID changed to fedcba9876543210/d
-/dpdk|INFO|DPDK Disabled - Use other_config:dpdk-init to enable/d
 /netlink_socket|INFO|netlink: could not enable listening to all nsid/d
 /netdev: Flow API/d
 /probe tc:/d
-/tc: Using policy/d']])
+/tc: Using policy/d
+/dpdk|INFO|/d
+/dpdk|WARN|/d']])
 ])
 
 # OVS_VSWITCHD_START([vsctl-args], [vsctl-output], [=override],
@@ -247,7 +248,7 @@ m4_define([OVS_VSWITCHD_START],
    AT_CHECK([add_of_br 0 $1 m4_if([$2], [], [], [| uuidfilt])], [0], [$2])
 ])
 
-# check_logs scans through all *.log files (except '*.log' and testsuite.log)
+# check_logs scans through all *.log files (except '*.log' and '*testsuite.log')
 # and reports all WARN, ERR, EMER log entries.  User can add custom sed filters
 # in $1.
 m4_divert_push([PREPARE_TESTS])
@@ -255,7 +256,7 @@ check_logs () {
     local logs
     for log in *.log; do
         case ${log} in # (
-            '*.log'|testsuite.log) ;; # (
+            '*.log'|*testsuite.log) ;; # (
             *) logs="${logs} ${log}" ;;
         esac
     done
diff --git a/tests/system-dpdk-macros.at b/tests/system-dpdk-macros.at
new file mode 100644
index 000000000..c1311121b
--- /dev/null
+++ b/tests/system-dpdk-macros.at
@@ -0,0 +1,108 @@
+AT_COPYRIGHT([Copyright (c) 2023 Red Hat, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at:
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.])
+
+m4_include([tests/system-userspace-macros.at])
+
+
+# OVS_DPDK_PRE_CHECK()
+#
+# Check prerequisites for DPDK tests. Following settings are checked:
+#  - Hugepages
+#
+m4_define([OVS_DPDK_PRE_CHECK],
+  [dnl Check Hugepages
+   AT_CHECK([cat /proc/meminfo], [], [stdout])
+   AT_SKIP_IF([grep -E 'HugePages_Free: *0' stdout], [], [stdout])
+   AT_CHECK([mount], [], [stdout])
+   AT_CHECK([grep 'hugetlbfs' stdout], [], [stdout], [])
+])
+
+m4_define([SYSTEM_DPDK_ALLOWED_LOGS],[
+])
+
+# OVS_TRAFFIC_VSWITCHD_START([vsctl-args], [vsctl-output], [=override])
+#
+# Creates a database and starts ovsdb-server, starts ovs-vswitchd
+# connected to that database, calls ovs-vsctl to create a bridge named
+# br0 with predictable settings, passing 'vsctl-args' as additional
+# commands to ovs-vsctl.  If 'vsctl-args' causes ovs-vsctl to provide
+# output (e.g. because it includes "create" commands) then 'vsctl-output'
+# specifies the expected output after filtering through uuidfilt.
+m4_define([OVS_TRAFFIC_VSWITCHD_START],
+  [OVS_DPDK_PRE_CHECK()
+   OVS_WAIT_WHILE([ip link show ovs-netdev])
+   _OVS_VSWITCHD_START([--disable-system], [-- set Open_vSwitch . other_config:dpdk-init=true])
+   dnl Add bridges, ports, etc.
+   OVS_WAIT_WHILE([ip link show br0])
+   AT_CHECK([ovs-vsctl -- _ADD_BR([br0]) -- $1 m4_if([$2], [], [], [| uuidfilt])], [0], [$2])
+])
+
+# OVS_TRAFFIC_VSWITCHD_STOP([WHITELIST], [extra_cmds])
+#
+# Gracefully stops ovs-vswitchd and ovsdb-server, checking their log files
+# for messages with severity WARN or higher and signaling an error if any
+# is present.  The optional WHITELIST may contain shell-quoted "sed"
+# commands to delete any warnings that are actually expected, e.g.:
+#
+#   OVS_TRAFFIC_VSWITCHD_STOP(["/expected error/d"])
+#
+# 'extra_cmds' are shell commands to be executed afte OVS_VSWITCHD_STOP() is
+# invoked. They can be used to perform additional cleanups such as name space
+# removal.
+m4_define([OVS_TRAFFIC_VSWITCHD_STOP],
+  [OVS_VSWITCHD_STOP([dnl
+$1";/EAL: No \(available\|free\) .*hugepages reported/d
+/TELEMETRY: No legacy callbacks, legacy socket not created/d
+/dpif(revalidator.*)|WARN|netdev@ovs-netdev: failed to.*proto=2.*/d
+/dpif_netdev(revalidator.*)|ERR|internal error parsing flow key.*proto=2.*/d
+/dpif_netlink.*Generic Netlink family 'ovs_datapath' does not exist. The Open vSwitch kernel module is probably not loaded./d
+/eth_dev_tap_create():  Disabling rte flow support: No such file or directory/d
+/eth_dev_tap_create(): .*: failed to create multiq qdisc./d
+/netdev_linux.*obtaining netdev stats via vport failed/d
+/qdisc_create_multiq(): Could not add multiq qdisc (2): No such file or directory/d
+/tap_mp_req_on_rxtx(): Failed to send start req to secondary/d
+/tap_nl_dump_ext_ack(): Specified qdisc kind is unknown/d"])
+   AT_CHECK([:; $2])
+])
+
+# ADD_VETH([port], [namespace], [ovs-br], [ip_addr] [mac_addr], [gateway],
+#          [ip_addr_flags] [ip6_addr] [gateway6])
+#
+# Fake a veth by creating a tap on kernel side and plug it in OVS using the
+# net/tap DPDK driver.
+#
+m4_define([ADD_VETH],
+  [AT_CHECK([ovs-vsctl add-port $3 ovs-$1 -- \
+             set interface ovs-$1 external-ids:iface-id="$1" -- \
+             set interface ovs-$1 type=dpdk -- \
+             set interface ovs-$1 options:n_rxq=2 -- \
+             set interface ovs-$1 options:dpdk-devargs=net_tap$1,iface=$1])
+   OVS_WAIT_UNTIL([ip link show dev $1 | grep -qw LOWER_UP])
+   AT_CHECK([ip link set $1 netns $2])
+   NS_CHECK_EXEC([$2], [ip addr add $4 dev $1 $7])
+   NS_CHECK_EXEC([$2], [ip link set dev $1 up])
+   if test -n "$5"; then
+     NS_CHECK_EXEC([$2], [ip link set dev $1 address $5])
+   fi
+   if test -n "$6"; then
+     NS_CHECK_EXEC([$2], [ip route add $6 dev $1])
+     NS_CHECK_EXEC([$2], [ip route add default via $6])
+   fi
+   if test -n "$8"; then
+     NS_CHECK_EXEC([$2], [ip addr add $8 dev $1])
+   fi
+   if test -n "$9"; then
+     NS_CHECK_EXEC([$2], [ip route add default via $9])
+   fi
+])
diff --git a/tests/system-dpdk-testsuite.at b/tests/system-dpdk-testsuite.at
new file mode 100644
index 000000000..72ddc3913
--- /dev/null
+++ b/tests/system-dpdk-testsuite.at
@@ -0,0 +1,25 @@
+AT_INIT
+
+AT_COPYRIGHT([Copyright (c) 2023 Red Hat, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at:
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.])
+
+m4_ifdef([AT_COLOR_TESTS], [AT_COLOR_TESTS])
+
+m4_include([tests/ovs-macros.at])
+m4_include([tests/ovsdb-macros.at])
+m4_include([tests/ofproto-macros.at])
+m4_include([tests/ovn-macros.at])
+m4_include([tests/system-common-macros.at])
+m4_include([tests/system-dpdk-macros.at])
+m4_include([tests/system-ovn.at])
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
index c2490008d..05c234edc 100644
--- a/tests/system-ovn.at
+++ b/tests/system-ovn.at
@@ -4189,7 +4189,7 @@ icmp,orig=(src=192.168.1.10,dst=192.168.1.20,id=<cleared>,type=8,code=0),reply=(
 check ovs-vsctl clear interface ovs-p1 external_ids
 
 # check conntrack zone is flushed
-check ovs-appctl dpctl/dump-conntrack zone=$zone_id
+OVS_WAIT_WHILE([ovs-appctl dpctl/dump-conntrack zone=$zone_id | grep .])
 
 OVS_APP_EXIT_AND_WAIT([ovn-controller])
 
@@ -6574,6 +6574,9 @@ ADD_NAMESPACES(public)
 ADD_VETH(public, public, br-public, "192.168.2.2/24", "f0:00:00:01:02:05")
 AT_CHECK([ovs-vsctl remove interface ovs-public external-ids iface-id=public])
 
+# Skip this test if the datapath interface does not support linux-htb
+AT_SKIP_IF([! ovs-appctl qos/show-types ovs-public | grep -q linux-htb])
+
 ADD_NAMESPACES(ext)
 ADD_VETH(ext, ext, br-ext, "192.168.3.2/24", "f0:00:00:01:02:06")
 AT_CHECK([ovs-vsctl remove interface ovs-ext external-ids iface-id=ext])
@@ -11202,6 +11205,12 @@ ADD_VETH(bar1, bar1, br-int, "2002::2/64", "f0:00:00:01:02:05", \
 ovn-nbctl lsp-add bar bar1 \
 -- lsp-set-addresses bar1 "f0:00:00:01:02:05 192.168.2.2 2002::2"
 
+# Warm up the datapath (needed to make the DPDK datapth happy)
+NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 192.168.2.2 | FORMAT_PING], \
+[0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+
 ovn-nbctl mirror-add mirror0 gre 1 to-lport 172.16.0.100
 ovn-nbctl lsp-attach-mirror bar1 mirror0
 
