[ovs-dev] [PATCH v3] dpif-netdev: Forwarding optimization for flows with a simple match.

Ilya Maximets i.maximets at ovn.org
Mon Aug 9 12:57:52 UTC 2021


There are cases where users might want simple forwarding or drop rules
for all packets received from a specific port, e.g ::

  "in_port=1,actions=2"
  "in_port=2,actions=IN_PORT"
  "in_port=3,vlan_tci=0x1234/0x1fff,actions=drop"
  "in_port=4,actions=push_vlan:0x8100,set_field:4196->vlan_vid,output:3"

There are also cases where complex OpenFlow rules can be simplified
down to datapath flows with very simple match criteria.

In theory, for very simple forwarding, OVS doesn't need to parse
packets at all in order to follow these rules.  "Simple match" lookup
optimization is intended to speed up packet forwarding in these cases.

Design:

Due to various implementation constraints userspace datapath has
following flow fields always in exact match (i.e. it's required to
match at least these fields of a packet even if the OF rule doesn't
need that):

  - recirc_id
  - in_port
  - packet_type
  - dl_type
  - vlan_tci (CFI + VID) - in most cases
  - nw_frag - for ip packets

Not all of these fields are related to packet itself.  We already
know the current 'recirc_id' and the 'in_port' before starting the
packet processing.  It also seems safe to assume that we're working
with Ethernet packets.  So, for the simple OF rule we need to match
only on 'dl_type', 'vlan_tci' and 'nw_frag'.

'in_port', 'dl_type', 'nw_frag' and 13 bits of 'vlan_tci' can be
combined in a single 64bit integer (mark) that can be used as a
hash in hash map.  We are using only VID and CFI form the 'vlan_tci',
flows that need to match on PCP will not qualify for the optimization.
Workaround for matching on non-existence of vlan updated to match on
CFI and VID only in order to qualify for the optimization.  CFI is
always set by OVS if vlan is present in a packet, so there is no need
to match on PCP in this case.  'nw_frag' takes 2 bits of PCP inside
the simple match mark.

New per-PMD flow table 'simple_match_table' introduced to store
simple match flows only.  'dp_netdev_flow_add' adds flow to the
usual 'flow_table' and to the 'simple_match_table' if the flow
meets following constraints:

  - 'recirc_id' in flow match is 0.
  - 'packet_type' in flow match is Ethernet.
  - Flow wildcards contains only minimal set of non-wildcarded fields
    (listed above).

If the number of flows for current 'in_port' in a regular 'flow_table'
equals number of flows for current 'in_port' in a 'simple_match_table',
we may use simple match optimization, because all the flows we have
are simple match flows.  This means that we only need to parse
'dl_type', 'vlan_tci' and 'nw_frag' to perform packet matching.
Now we make the unique flow mark from the 'in_port', 'dl_type',
'nw_frag' and 'vlan_tci' and looking for it in the 'simple_match_table'.
On successful lookup we don't need to run full 'miniflow_extract()'.

Unsuccessful lookup technically means that we have no suitable flow
in the datapath and upcall will be required.  So, in this case EMC and
SMC lookups are disabled.  We may optimize this path in the future by
bypassing the dpcls lookup too.

Performance improvement of this solution on a 'simple match' flows
should be comparable with partial HW offloading, because it parses same
packet fields and uses similar flow lookup scheme.
However, unlike partial HW offloading, it works for all port types
including virtual ones.

Performance results when compared to EMC:

Test setup:

             virtio-user   OVS    virtio-user
  Testpmd1  ------------>  pmd1  ------------>  Testpmd2
  (txonly)       x<------  pmd2  <------------ (mac swap)

Single stream of 64byte packets.  Actions:
  in_port=vhost0,actions=vhost1
  in_port=vhost1,actions=vhost0

Stats collected from pmd1 and pmd2, so there are 2 scenarios:
Virt-to-Virt   :     Testpmd1 ------> pmd1 ------> Testpmd2.
Virt-to-NoCopy :     Testpmd2 ------> pmd2 --->x   Testpmd1.
Here the packet sent from pmd2 to Testpmd1 is always dropped, because
the virtqueue is full since Testpmd1 is in txonly mode and doesn't
receive any packets.  This should be closer to the performance of a
VM-to-Phy scenario.

Test performed on machine with Intel Xeon CPU E5-2690 v4 @ 2.60GHz.
Table below represents improvement in throughput when compared to EMC.

 +----------------+------------------------+------------------------+
 |                |    Default (-g -O2)    | "-Ofast -march=native" |
 |   Scenario     +------------+-----------+------------+-----------+
 |                |     GCC    |   Clang   |     GCC    |   Clang   |
 +----------------+------------+-----------+------------+-----------+
 | Virt-to-Virt   |    +18.9%  |   +25.5%  |    +10.8%  |   +16.7%  |
 | Virt-to-NoCopy |    +24.3%  |   +33.7%  |    +14.9%  |   +22.0%  |
 +----------------+------------+-----------+------------+-----------+

For Phy-to-Phy case performance improvement should be even higher, but
it's not the main use-case for this functionality.  Performance
difference for the non-simple flows is within a margin of error.

Signed-off-by: Ilya Maximets <i.maximets at ovn.org>
---

Version 3:
  - Removed artificial restriction on flow actions.  Any set of actions
    is supported.  Hence the patch re-named from "direct output" to
    "simple match".
  - VLAN VID+CFI added to the mark.  Previous implementation from v1/v2
    was not correct in a way that it ignored match on VLAN if match on
    non-existence of VLAN was requested.  Workaround for netlink
    expression of the match on non-existance of VLAN improved to avoid
    match on PCP, so we have extra bits inside the mark to fit
    fragmentation flags.  Technically, we can keep only match on CFI,
    but we have enough bits, so we don't need to reduce the mask
    further.  We parsed VLAN header from a packet anyway in v1 and v2,
    so this doesn't really affect performance of the solution, but
    increases the variety of flows that can be optimized.
  - Since we're matching on VLAN, the optimization now works for flows
    with VLAN matches (VID+CFI, no PCP matches).
  - Added statistics, documentation and performance test results.

Version 2 (Harsha):
  - Rebase on 2.15
  - Added a coverage counter.
  - https://patchwork.ozlabs.org/project/openvswitch/patch/20210524065140.31891-1-sriharsha.basavapatna@broadcom.com/

 Documentation/topics/dpdk/bridge.rst |  24 ++
 NEWS                                 |   3 +
 lib/dpif-netdev-avx512.c             |   3 +-
 lib/dpif-netdev-perf.c               |  41 ++--
 lib/dpif-netdev-perf.h               |   1 +
 lib/dpif-netdev-private-flow.h       |   5 +-
 lib/dpif-netdev-private-thread.h     |  13 +-
 lib/dpif-netdev-unixctl.man          |  12 +-
 lib/dpif-netdev.c                    | 316 +++++++++++++++++++++++----
 lib/flow.c                           |  22 +-
 lib/flow.h                           |   3 +-
 lib/netdev-offload-dpdk.c            |   2 +-
 tests/dpif-netdev.at                 |  16 +-
 tests/nsh.at                         |   2 +-
 tests/pmd.at                         |   6 +-
 15 files changed, 386 insertions(+), 83 deletions(-)

diff --git a/Documentation/topics/dpdk/bridge.rst b/Documentation/topics/dpdk/bridge.rst
index f422eb5e9..00fce8355 100644
--- a/Documentation/topics/dpdk/bridge.rst
+++ b/Documentation/topics/dpdk/bridge.rst
@@ -81,6 +81,30 @@ using the following command::
 
     $ ovs-vsctl get Interface <iface> statistics
 
+Simple Match Lookup
+-------------------
+
+There are cases where users might want simple forwarding or drop rules for all
+packets received from a specific port, e.g ::
+
+    in_port=1,actions=2
+    in_port=2,actions=IN_PORT
+    in_port=3,vlan_tci=0x1234/0x1fff,actions=drop
+    in_port=4,actions=push_vlan:0x8100,set_field:4196->vlan_vid,output:3
+
+There are also cases where complex OpenFlow rules can be simplified down to
+datapath flows with very simple match criteria.
+
+In theory, for very simple forwarding, OVS doesn't need to parse packets at all
+in order to follow these rules.  In practice, due to various implementation
+constraints, userspace datapath has to match at least on a small set of packet
+fileds.  Some matching criteria (for example, ingress port) are not related to
+the packet itself and others (for example, VLAN tag or Ethernet type) can be
+extracted without fully parsing the packet.  This allows OVS to significantly
+speed up packet forwarding for these flows with simple match criteria.
+Statistics on the number of packets matched in this way can be found in a
+`simple match hits` counter of `ovs-appctl dpif-netdev/pmd-stats-show` command.
+
 EMC Insertion Probability
 -------------------------
 
diff --git a/NEWS b/NEWS
index 26920e215..ae5459dd0 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,8 @@
 Post-v2.16.0
 ---------------------
+   - Userspace datapath:
+     * Optimized flow lookups for datapath flows with simple match criteria.
+       See 'Simple Match Lookup' in Documentation/topics/dpdk/bridge.rst.
    - DPDK:
      * EAL argument --socket-mem is no longer configured by default upon
        start-up.  If dpdk-socket-mem and dpdk-alloc-mem are not specified,
diff --git a/lib/dpif-netdev-avx512.c b/lib/dpif-netdev-avx512.c
index 544d36903..d176ecf5b 100644
--- a/lib/dpif-netdev-avx512.c
+++ b/lib/dpif-netdev-avx512.c
@@ -197,7 +197,8 @@ dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd,
                 if (mfex_hit) {
                     pkt_meta[i].tcp_flags = miniflow_get_tcp_flags(&key->mf);
                 } else {
-                    pkt_meta[i].tcp_flags = parse_tcp_flags(packet);
+                    pkt_meta[i].tcp_flags = parse_tcp_flags(packet,
+                                                            NULL, NULL, NULL);
                 }
 
                 pkt_meta[i].bytes = dp_packet_size(packet);
diff --git a/lib/dpif-netdev-perf.c b/lib/dpif-netdev-perf.c
index d7676ea2b..a2a7d8f0b 100644
--- a/lib/dpif-netdev-perf.c
+++ b/lib/dpif-netdev-perf.c
@@ -232,10 +232,10 @@ pmd_perf_format_overall_stats(struct ds *str, struct pmd_perf_stats *s,
     uint64_t busy_iter = tot_iter >= idle_iter ? tot_iter - idle_iter : 0;
 
     ds_put_format(str,
-            "  Iterations:        %12"PRIu64"  (%.2f us/it)\n"
-            "  - Used TSC cycles: %12"PRIu64"  (%5.1f %% of total cycles)\n"
-            "  - idle iterations: %12"PRIu64"  (%5.1f %% of used cycles)\n"
-            "  - busy iterations: %12"PRIu64"  (%5.1f %% of used cycles)\n",
+            "  Iterations:         %12"PRIu64"  (%.2f us/it)\n"
+            "  - Used TSC cycles:  %12"PRIu64"  (%5.1f %% of total cycles)\n"
+            "  - idle iterations:  %12"PRIu64"  (%5.1f %% of used cycles)\n"
+            "  - busy iterations:  %12"PRIu64"  (%5.1f %% of used cycles)\n",
             tot_iter, tot_cycles * us_per_cycle / tot_iter,
             tot_cycles, 100.0 * (tot_cycles / duration) / tsc_hz,
             idle_iter,
@@ -244,16 +244,17 @@ pmd_perf_format_overall_stats(struct ds *str, struct pmd_perf_stats *s,
             100.0 * stats[PMD_CYCLES_ITER_BUSY] / tot_cycles);
     if (rx_packets > 0) {
         ds_put_format(str,
-            "  Rx packets:        %12"PRIu64"  (%.0f Kpps, %.0f cycles/pkt)\n"
-            "  Datapath passes:   %12"PRIu64"  (%.2f passes/pkt)\n"
-            "  - PHWOL hits:      %12"PRIu64"  (%5.1f %%)\n"
-            "  - MFEX Opt hits:   %12"PRIu64"  (%5.1f %%)\n"
-            "  - EMC hits:        %12"PRIu64"  (%5.1f %%)\n"
-            "  - SMC hits:        %12"PRIu64"  (%5.1f %%)\n"
-            "  - Megaflow hits:   %12"PRIu64"  (%5.1f %%, %.2f "
-                                                "subtbl lookups/hit)\n"
-            "  - Upcalls:         %12"PRIu64"  (%5.1f %%, %.1f us/upcall)\n"
-            "  - Lost upcalls:    %12"PRIu64"  (%5.1f %%)\n",
+            "  Rx packets:         %12"PRIu64"  (%.0f Kpps, %.0f cycles/pkt)\n"
+            "  Datapath passes:    %12"PRIu64"  (%.2f passes/pkt)\n"
+            "  - PHWOL hits:       %12"PRIu64"  (%5.1f %%)\n"
+            "  - MFEX Opt hits:    %12"PRIu64"  (%5.1f %%)\n"
+            "  - Simple Match hits:%12"PRIu64"  (%5.1f %%)\n"
+            "  - EMC hits:         %12"PRIu64"  (%5.1f %%)\n"
+            "  - SMC hits:         %12"PRIu64"  (%5.1f %%)\n"
+            "  - Megaflow hits:    %12"PRIu64"  (%5.1f %%, %.2f "
+                                                 "subtbl lookups/hit)\n"
+            "  - Upcalls:          %12"PRIu64"  (%5.1f %%, %.1f us/upcall)\n"
+            "  - Lost upcalls:     %12"PRIu64"  (%5.1f %%)\n",
             rx_packets, (rx_packets / duration) / 1000,
             1.0 * stats[PMD_CYCLES_ITER_BUSY] / rx_packets,
             passes, rx_packets ? 1.0 * passes / rx_packets : 0,
@@ -261,6 +262,8 @@ pmd_perf_format_overall_stats(struct ds *str, struct pmd_perf_stats *s,
             100.0 * stats[PMD_STAT_PHWOL_HIT] / passes,
             stats[PMD_STAT_MFEX_OPT_HIT],
             100.0 * stats[PMD_STAT_MFEX_OPT_HIT] / passes,
+            stats[PMD_STAT_SIMPLE_HIT],
+            100.0 * stats[PMD_STAT_SIMPLE_HIT] / passes,
             stats[PMD_STAT_EXACT_HIT],
             100.0 * stats[PMD_STAT_EXACT_HIT] / passes,
             stats[PMD_STAT_SMC_HIT],
@@ -275,16 +278,18 @@ pmd_perf_format_overall_stats(struct ds *str, struct pmd_perf_stats *s,
             stats[PMD_STAT_LOST],
             100.0 * stats[PMD_STAT_LOST] / passes);
     } else {
-        ds_put_format(str, "  Rx packets:        %12d\n", 0);
+        ds_put_format(str,
+            "  Rx packets:         %12d\n", 0);
     }
     if (tx_packets > 0) {
         ds_put_format(str,
-            "  Tx packets:        %12"PRIu64"  (%.0f Kpps)\n"
-            "  Tx batches:        %12"PRIu64"  (%.2f pkts/batch)\n",
+            "  Tx packets:         %12"PRIu64"  (%.0f Kpps)\n"
+            "  Tx batches:         %12"PRIu64"  (%.2f pkts/batch)\n",
             tx_packets, (tx_packets / duration) / 1000,
             tx_batches, 1.0 * tx_packets / tx_batches);
     } else {
-        ds_put_format(str, "  Tx packets:        %12d\n\n", 0);
+        ds_put_format(str,
+            "  Tx packets:         %12d\n\n", 0);
     }
 }
 
diff --git a/lib/dpif-netdev-perf.h b/lib/dpif-netdev-perf.h
index 834c26260..9673dddd8 100644
--- a/lib/dpif-netdev-perf.h
+++ b/lib/dpif-netdev-perf.h
@@ -58,6 +58,7 @@ extern "C" {
 enum pmd_stat_type {
     PMD_STAT_PHWOL_HIT,     /* Packets that had a partial HWOL hit (phwol). */
     PMD_STAT_MFEX_OPT_HIT,  /* Packets that had miniflow optimized match. */
+    PMD_STAT_SIMPLE_HIT,    /* Packets that had a simple match hit. */
     PMD_STAT_EXACT_HIT,     /* Packets that had an exact match (emc). */
     PMD_STAT_SMC_HIT,       /* Packets that had a sig match hit (SMC). */
     PMD_STAT_MASKED_HIT,    /* Packets that matched in the flow table. */
diff --git a/lib/dpif-netdev-private-flow.h b/lib/dpif-netdev-private-flow.h
index 303066067..66016eb09 100644
--- a/lib/dpif-netdev-private-flow.h
+++ b/lib/dpif-netdev-private-flow.h
@@ -87,6 +87,8 @@ struct dp_netdev_flow {
     /* Hash table index by unmasked flow. */
     const struct cmap_node node; /* In owning dp_netdev_pmd_thread's */
                                  /* 'flow_table'. */
+    const struct cmap_node simple_match_node; /* In dp_netdev_pmd_thread's
+                                                 'simple_match_table'. */
     const struct cmap_node mark_node; /* In owning flow_mark's mark_to_flow */
     const ovs_u128 ufid;         /* Unique flow identifier. */
     const ovs_u128 mega_ufid;    /* Unique mega flow identifier. */
@@ -100,7 +102,8 @@ struct dp_netdev_flow {
     struct ovs_refcount ref_cnt;
 
     bool dead;
-    uint32_t mark;               /* Unique flow mark assigned to a flow */
+    uint32_t mark;               /* Unique flow mark for netdev offloading. */
+    uint64_t simple_match_mark;  /* Unique flow mark for the simple match. */
 
     /* Statistics. */
     struct dp_netdev_flow_stats stats;
diff --git a/lib/dpif-netdev-private-thread.h b/lib/dpif-netdev-private-thread.h
index a782d9678..4463b1035 100644
--- a/lib/dpif-netdev-private-thread.h
+++ b/lib/dpif-netdev-private-thread.h
@@ -26,6 +26,7 @@
 #include <stdbool.h>
 #include <stdint.h>
 
+#include "ccmap.h"
 #include "cmap.h"
 
 #include "dpif-netdev-private-dfc.h"
@@ -86,12 +87,18 @@ struct dp_netdev_pmd_thread {
 
     /* Flow-Table and classifiers
      *
-     * Writers of 'flow_table' must take the 'flow_mutex'.  Corresponding
-     * changes to 'classifiers' must be made while still holding the
-     * 'flow_mutex'.
+     * Writers of 'flow_table'/'simple_match_table' and their n* ccmap's must
+     * take the 'flow_mutex'.  Corresponding changes to 'classifiers' must be
+     * made while still holding the 'flow_mutex'.
      */
     struct ovs_mutex flow_mutex;
     struct cmap flow_table OVS_GUARDED; /* Flow table. */
+    struct cmap simple_match_table OVS_GUARDED; /* Flow table with simple
+                                                   match flows only. */
+    /* Number of flows in the 'flow_table' per in_port. */
+    struct ccmap n_flows OVS_GUARDED;
+    /* Number of flows in the 'simple_match_table' per in_port. */
+    struct ccmap n_simple_flows OVS_GUARDED;
 
     /* One classifier per in_port polled by the pmd */
     struct cmap classifiers;
diff --git a/lib/dpif-netdev-unixctl.man b/lib/dpif-netdev-unixctl.man
index 80304ad35..4848079c6 100644
--- a/lib/dpif-netdev-unixctl.man
+++ b/lib/dpif-netdev-unixctl.man
@@ -11,10 +11,11 @@ Shows performance statistics for one or all pmd threads of the datapath
 \fIdp\fR. The special thread "main" sums up the statistics of every non pmd
 thread.
 
-The sum of "emc hits", "smc hits", "megaflow hits" and "miss" is the number of
-packet lookups performed by the datapath. Beware that a recirculated packet
-experiences one additional lookup per recirculation, so there may be
-more lookups than forwarded packets in the datapath.
+The sum of "phwol hits", "simple match hits", "emc hits", "smc hits",
+"megaflow hits" and "miss" is the number of packet lookups performed by the
+datapath. Beware that a recirculated packet experiences one additional lookup
+per recirculation, so there may be more lookups than forwarded packets in the
+datapath.
 
 The MFEX Opt hits displays the number of packets that are processed by the
 optimized miniflow extract implementations.
@@ -140,8 +141,9 @@ pmd thread numa_id 0 core_id 1:
   Datapath passes:        3599415  (1.50 passes/pkt)
   - PHWOL hits:                 0  (  0.0 %)
   - MFEX Opt hits:        3570133  ( 99.2 %)
+  - Simple Match hits:          0  (  0.0 %)
   - EMC hits:              336472  (  9.3 %)
-  - SMC hits:                   0  ( 0.0 %)
+  - SMC hits:                   0  (  0.0 %)
   - Megaflow hits:        3262943  ( 90.7 %, 1.00 subtbl lookups/hit)
   - Upcalls:                    0  (  0.0 %, 0.0 us/upcall)
   - Lost upcalls:               0  (  0.0 %)
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 03f460c7d..f038deff1 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -35,6 +35,7 @@
 #include <unistd.h>
 
 #include "bitmap.h"
+#include "ccmap.h"
 #include "cmap.h"
 #include "conntrack.h"
 #include "conntrack-tp.h"
@@ -559,6 +560,20 @@ 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_simple_match_insert(struct dp_netdev_pmd_thread *pmd,
+                                          struct dp_netdev_flow *flow)
+    OVS_REQUIRES(pmd->flow_mutex);
+static void dp_netdev_simple_match_remove(struct dp_netdev_pmd_thread *pmd,
+                                          struct dp_netdev_flow *flow)
+    OVS_REQUIRES(pmd->flow_mutex);
+
+static bool dp_netdev_flow_is_simple_match(const struct match *);
+static bool dp_netdev_simple_match_enabled(const struct dp_netdev_pmd_thread *,
+                                           odp_port_t in_port);
+static struct dp_netdev_flow *dp_netdev_simple_match_lookup(
+    const struct dp_netdev_pmd_thread *,
+    odp_port_t in_port, ovs_be16 dp_type, uint8_t nw_frag, ovs_be16 vlan_tci);
+
 /* Updates the time in PMD threads context and should be called in three cases:
  *
  *     1. PMD structure initialization:
@@ -658,6 +673,7 @@ pmd_info_show_stats(struct ds *reply,
                   "  avg. datapath passes per packet: %.02f\n"
                   "  phwol hits: %"PRIu64"\n"
                   "  mfex opt hits: %"PRIu64"\n"
+                  "  simple match hits: %"PRIu64"\n"
                   "  emc hits: %"PRIu64"\n"
                   "  smc hits: %"PRIu64"\n"
                   "  megaflow hits: %"PRIu64"\n"
@@ -667,8 +683,11 @@ pmd_info_show_stats(struct ds *reply,
                   "  avg. packets per output batch: %.02f\n",
                   total_packets, stats[PMD_STAT_RECIRC],
                   passes_per_pkt, stats[PMD_STAT_PHWOL_HIT],
-                  stats[PMD_STAT_MFEX_OPT_HIT], stats[PMD_STAT_EXACT_HIT],
-                  stats[PMD_STAT_SMC_HIT], stats[PMD_STAT_MASKED_HIT],
+                  stats[PMD_STAT_MFEX_OPT_HIT],
+                  stats[PMD_STAT_SIMPLE_HIT],
+                  stats[PMD_STAT_EXACT_HIT],
+                  stats[PMD_STAT_SMC_HIT],
+                  stats[PMD_STAT_MASKED_HIT],
                   lookups_per_hit, stats[PMD_STAT_MISS], stats[PMD_STAT_LOST],
                   packets_per_batch);
 
@@ -1949,6 +1968,7 @@ dpif_netdev_get_stats(const struct dpif *dpif, struct dpif_dp_stats *stats)
         stats->n_flows += cmap_count(&pmd->flow_table);
         pmd_perf_read_counters(&pmd->perf_stats, pmd_stats);
         stats->n_hit += pmd_stats[PMD_STAT_PHWOL_HIT];
+        stats->n_hit += pmd_stats[PMD_STAT_SIMPLE_HIT];
         stats->n_hit += pmd_stats[PMD_STAT_EXACT_HIT];
         stats->n_hit += pmd_stats[PMD_STAT_SMC_HIT];
         stats->n_hit += pmd_stats[PMD_STAT_MASKED_HIT];
@@ -2816,7 +2836,9 @@ dp_netdev_pmd_remove_flow(struct dp_netdev_pmd_thread *pmd,
     cls = dp_netdev_pmd_lookup_dpcls(pmd, in_port);
     ovs_assert(cls != NULL);
     dpcls_remove(cls, &flow->cr);
+    dp_netdev_simple_match_remove(pmd, flow);
     cmap_remove(&pmd->flow_table, node, dp_netdev_flow_hash(&flow->ufid));
+    ccmap_dec(&pmd->n_flows, odp_to_u32(in_port));
     if (flow->mark != INVALID_FLOW_MARK) {
         queue_netdev_flow_del(pmd, flow);
     }
@@ -3572,6 +3594,177 @@ dp_netdev_get_mega_ufid(const struct match *match, ovs_u128 *mega_ufid)
     odp_flow_key_hash(&masked_flow, sizeof masked_flow, mega_ufid);
 }
 
+static uint64_t
+dp_netdev_simple_match_mark(odp_port_t in_port, ovs_be16 dl_type,
+                            uint8_t nw_frag, ovs_be16 vlan_tci)
+{
+    /* Simple Match Mark:
+     *
+     * BE:
+     * +-----------------+-------------++---------+---+-----------+
+     * |     in_port     |   dl_type   || nw_frag |CFI|  VID(12)  |
+     * +-----------------+-------------++---------+---+-----------+
+     * 0                 32          47 49         51  52     63
+     *
+     * LE:
+     * +-----------------+-------------+------++-------+---+------+
+     * |     in_port     |   dl_type   |VID(8)||nw_frag|CFI|VID(4)|
+     * +-----------------+-------------+------++-------+---+------+
+     * 0                 32          47 48  55  57   59 60  61   63
+     *
+     *         Big Endian              Little Endian
+     * in_port : 32 bits [ 0..31]  in_port : 32 bits [ 0..31]
+     * dl_type : 16 bits [32..47]  dl_type : 16 bits [32..47]
+     * <empty> :  1 bit  [48..48]  vlan VID:  8 bits [48..55]
+     * nw_frag :  2 bits [49..50]  <empty> :  1 bit  [56..56]
+     * vlan CFI:  1 bit  [51..51]  nw_frag :  2 bits [57..59]
+     * vlan VID: 12 bits [52..63]  vlan CFI:  1 bit  [60..60]
+     *                             vlan VID:  4 bits [61..63]
+     *
+     * Layout is different for LE and BE in order to save a couple of
+     * network to host translations.
+     * */
+    return ((uint64_t) odp_to_u32(in_port) << 32)
+           | ((OVS_FORCE uint32_t) dl_type << 16)
+#if WORDS_BIGENDIAN
+           | (((uint16_t) nw_frag & FLOW_NW_FRAG_MASK) << VLAN_PCP_SHIFT)
+#else
+           | ((nw_frag & FLOW_NW_FRAG_MASK) << (VLAN_PCP_SHIFT - 8))
+#endif
+           | (OVS_FORCE uint16_t) (vlan_tci & htons(VLAN_VID_MASK | VLAN_CFI));
+}
+
+static struct dp_netdev_flow *
+dp_netdev_simple_match_lookup(const struct dp_netdev_pmd_thread *pmd,
+                              odp_port_t in_port, ovs_be16 dl_type,
+                              uint8_t nw_frag, ovs_be16 vlan_tci)
+{
+    uint64_t mark = dp_netdev_simple_match_mark(in_port, dl_type,
+                                                nw_frag, vlan_tci);
+    uint32_t hash = hash_uint64(mark);
+    struct dp_netdev_flow *flow;
+    bool found = false;
+
+    CMAP_FOR_EACH_WITH_HASH (flow, simple_match_node,
+                             hash, &pmd->simple_match_table) {
+        if (flow->simple_match_mark == mark) {
+            found = true;
+            break;
+        }
+    }
+    return found ? flow : NULL;
+}
+
+static bool
+dp_netdev_simple_match_enabled(const struct dp_netdev_pmd_thread *pmd,
+                               odp_port_t in_port)
+{
+    return ccmap_find(&pmd->n_flows, odp_to_u32(in_port))
+           == ccmap_find(&pmd->n_simple_flows, odp_to_u32(in_port));
+}
+
+static void
+dp_netdev_simple_match_insert(struct dp_netdev_pmd_thread *pmd,
+                              struct dp_netdev_flow *dp_flow)
+    OVS_REQUIRES(pmd->flow_mutex)
+{
+    odp_port_t in_port = dp_flow->flow.in_port.odp_port;
+    ovs_be16 vlan_tci = dp_flow->flow.vlans[0].tci;
+    ovs_be16 dl_type = dp_flow->flow.dl_type;
+    uint8_t nw_frag = dp_flow->flow.nw_frag;
+
+    if (!dp_netdev_flow_ref(dp_flow)) {
+        return;
+    }
+
+    /* Avoid double insertion.  Should not happen in practice. */
+    dp_netdev_simple_match_remove(pmd, dp_flow);
+
+    uint64_t mark = dp_netdev_simple_match_mark(in_port, dl_type,
+                                                nw_frag, vlan_tci);
+    uint32_t hash = hash_uint64(mark);
+
+    dp_flow->simple_match_mark = mark;
+    cmap_insert(&pmd->simple_match_table,
+                CONST_CAST(struct cmap_node *, &dp_flow->simple_match_node),
+                hash);
+    ccmap_inc(&pmd->n_simple_flows, odp_to_u32(in_port));
+
+    VLOG_DBG("Simple match insert: "
+             "core_id(%d),in_port(%"PRIu32"),mark(0x%016"PRIx64").",
+             pmd->core_id, in_port, mark);
+}
+
+static void
+dp_netdev_simple_match_remove(struct dp_netdev_pmd_thread *pmd,
+                               struct dp_netdev_flow *dp_flow)
+    OVS_REQUIRES(pmd->flow_mutex)
+{
+    odp_port_t in_port = dp_flow->flow.in_port.odp_port;
+    ovs_be16 vlan_tci = dp_flow->flow.vlans[0].tci;
+    ovs_be16 dl_type = dp_flow->flow.dl_type;
+    uint8_t nw_frag = dp_flow->flow.nw_frag;
+    struct dp_netdev_flow *flow;
+    uint64_t mark = dp_netdev_simple_match_mark(in_port, dl_type,
+                                                nw_frag, vlan_tci);
+    uint32_t hash = hash_uint64(mark);
+
+    flow = dp_netdev_simple_match_lookup(pmd, in_port, dl_type,
+                                         nw_frag, vlan_tci);
+    if (flow == dp_flow) {
+        VLOG_DBG("Simple match remove: "
+                 "core_id(%d),in_port(%"PRIu32"),mark(0x%016"PRIx64").",
+                 pmd->core_id, in_port, mark);
+        cmap_remove(&pmd->simple_match_table,
+                    CONST_CAST(struct cmap_node *, &flow->simple_match_node),
+                    hash);
+        ccmap_dec(&pmd->n_simple_flows, odp_to_u32(in_port));
+        dp_netdev_flow_unref(flow);
+    }
+}
+
+static bool
+dp_netdev_flow_is_simple_match(const struct match *match)
+{
+    const struct flow *flow = &match->flow;
+    const struct flow_wildcards *wc = &match->wc;
+
+    if (flow->recirc_id || flow->packet_type != htonl(PT_ETH)) {
+        return false;
+    }
+
+    /* Check that flow matches only minimal set of fields that always set.
+     * Also checking that VLAN VID+CFI is an exact match, because these
+     * are not mandatory and could be masked. */
+    struct flow_wildcards *minimal = xmalloc(sizeof *minimal);
+    ovs_be16 vlan_tci_mask = htons(VLAN_VID_MASK | VLAN_CFI);
+
+    flow_wildcards_init_catchall(minimal);
+    /* 'dpif-netdev' always has following in exact match:
+     *   - recirc_id                   <-- recirc_id == 0 checked on input.
+     *   - in_port                     <-- Will be checked on input.
+     *   - packet_type                 <-- Assuming all packets are PT_ETH.
+     *   - dl_type                     <-- Need to match with.
+     *   - vlan_tci                    <-- Need to match with.
+     *   - and nw_frag for ip packets. <-- Need to match with.
+     */
+    WC_MASK_FIELD(minimal, recirc_id);
+    WC_MASK_FIELD(minimal, in_port);
+    WC_MASK_FIELD(minimal, packet_type);
+    WC_MASK_FIELD(minimal, dl_type);
+    WC_MASK_FIELD_MASK(minimal, vlans[0].tci, vlan_tci_mask);
+    WC_MASK_FIELD_MASK(minimal, nw_frag, FLOW_NW_FRAG_MASK);
+
+    if (flow_wildcards_has_extra(minimal, wc)
+        || wc->masks.vlans[0].tci != vlan_tci_mask) {
+        free(minimal);
+        return false;
+    }
+    free(minimal);
+
+    return true;
+}
+
 static struct dp_netdev_flow *
 dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd,
                    struct match *match, const ovs_u128 *ufid,
@@ -3641,6 +3834,11 @@ 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);
+    }
 
     queue_netdev_flow_put(pmd, flow, match, actions, actions_len,
                           orig_in_port, DP_NETDEV_FLOW_OFFLOAD_OP_ADD);
@@ -3766,7 +3964,7 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put)
      * Netlink and struct flow representations, we have to do the same
      * here.  This must be in sync with 'match' in handle_packet_upcall(). */
     if (!match.wc.masks.vlans[0].tci) {
-        match.wc.masks.vlans[0].tci = htons(0xffff);
+        match.wc.masks.vlans[0].tci = htons(VLAN_VID_MASK | VLAN_CFI);
     }
 
     /* Must produce a netdev_flow_key for lookup.
@@ -6710,6 +6908,9 @@ dp_netdev_configure_pmd(struct dp_netdev_pmd_thread *pmd, struct dp_netdev *dp,
     ovs_mutex_init(&pmd->bond_mutex);
     cmap_init(&pmd->flow_table);
     cmap_init(&pmd->classifiers);
+    cmap_init(&pmd->simple_match_table);
+    ccmap_init(&pmd->n_flows);
+    ccmap_init(&pmd->n_simple_flows);
     pmd->ctx.last_rxq = NULL;
     pmd_thread_ctx_time_update(pmd);
     pmd->next_optimization = pmd->ctx.now + DPCLS_OPTIMIZATION_INTERVAL;
@@ -6763,6 +6964,9 @@ dp_netdev_destroy_pmd(struct dp_netdev_pmd_thread *pmd)
     }
     cmap_destroy(&pmd->classifiers);
     cmap_destroy(&pmd->flow_table);
+    cmap_destroy(&pmd->simple_match_table);
+    ccmap_destroy(&pmd->n_flows);
+    ccmap_destroy(&pmd->n_simple_flows);
     ovs_mutex_destroy(&pmd->flow_mutex);
     seq_destroy(pmd->reload_seq);
     ovs_mutex_destroy(&pmd->port_mutex);
@@ -7296,6 +7500,33 @@ dp_netdev_hw_flow(const struct dp_netdev_pmd_thread *pmd,
     return 0;
 }
 
+/* Enqueues already classified packet into per-flow batches or the flow map,
+ * depending on the fact if batching enabled. */
+static inline void
+dfc_processing_enqueue_classified_packet(struct dp_packet *packet,
+                                         struct dp_netdev_flow *flow,
+                                         uint16_t tcp_flags,
+                                         bool batch_enable,
+                                         struct packet_batch_per_flow *batches,
+                                         size_t *n_batches,
+                                         struct dp_packet_flow_map *flow_map,
+                                         size_t *map_cnt)
+
+{
+    if (OVS_LIKELY(batch_enable)) {
+        dp_netdev_queue_batches(packet, flow, tcp_flags, batches,
+                                n_batches);
+    } else {
+        /* Flow batching should be performed only after fast-path
+         * processing is also completed for packets with emc miss
+         * or else it will result in reordering of packets with
+         * same datapath flows. */
+        packet_enqueue_to_flow_map(packet, flow, tcp_flags,
+                                   flow_map, (*map_cnt)++);
+    }
+
+}
+
 /* Try to process all ('cnt') the 'packets' using only the datapath flow cache
  * 'pmd->flow_cache'. If a flow is not found for a packet 'packets[i]', the
  * miniflow is copied into 'keys' and the packet pointer is moved at the
@@ -7321,25 +7552,32 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
                size_t *n_flows, uint8_t *index_map,
                bool md_is_valid, odp_port_t port_no)
 {
-    struct netdev_flow_key *key = &keys[0];
-    size_t n_missed = 0, n_emc_hit = 0, n_phwol_hit = 0,  n_mfex_opt_hit = 0;
+    const bool netdev_flow_api = netdev_is_flow_api_enabled();
+    const uint32_t recirc_depth = *recirc_depth_get();
+    const size_t cnt = dp_packet_batch_size(packets_);
+    size_t n_missed = 0, n_emc_hit = 0, n_phwol_hit = 0;
+    size_t n_mfex_opt_hit = 0, n_simple_hit = 0;
     struct dfc_cache *cache = &pmd->flow_cache;
+    struct netdev_flow_key *key = &keys[0];
     struct dp_packet *packet;
-    const size_t cnt = dp_packet_batch_size(packets_);
-    uint32_t cur_min = pmd->ctx.emc_insert_min;
-    const uint32_t recirc_depth = *recirc_depth_get();
-    const bool netdev_flow_api = netdev_is_flow_api_enabled();
-    int i;
-    uint16_t tcp_flags;
     size_t map_cnt = 0;
     bool batch_enable = true;
 
+    const bool simple_match_enabled =
+        !md_is_valid && dp_netdev_simple_match_enabled(pmd, port_no);
+    /* 'simple_match_table' is a full flow table.  If the flow is not there,
+     * upcall is required, and there is no chance to find a match in caches. */
+    const bool smc_enable_db = !simple_match_enabled && pmd->ctx.smc_enable_db;
+    const uint32_t cur_min = simple_match_enabled
+                             ? 0 : pmd->ctx.emc_insert_min;
+
     pmd_perf_update_counter(&pmd->perf_stats,
                             md_is_valid ? PMD_STAT_RECIRC : PMD_STAT_RECV,
                             cnt);
-
+    int i;
     DP_PACKET_BATCH_REFILL_FOR_EACH (i, cnt, packet, packets_) {
-        struct dp_netdev_flow *flow;
+        struct dp_netdev_flow *flow = NULL;
+        uint16_t tcp_flags;
 
         if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) {
             dp_packet_delete(packet);
@@ -7366,19 +7604,27 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
                 continue;
             }
             if (OVS_LIKELY(flow)) {
-                tcp_flags = parse_tcp_flags(packet);
+                tcp_flags = parse_tcp_flags(packet, NULL, NULL, NULL);
                 n_phwol_hit++;
-                if (OVS_LIKELY(batch_enable)) {
-                    dp_netdev_queue_batches(packet, flow, tcp_flags, batches,
-                                            n_batches);
-                } else {
-                    /* Flow batching should be performed only after fast-path
-                     * processing is also completed for packets with emc miss
-                     * or else it will result in reordering of packets with
-                     * same datapath flows. */
-                    packet_enqueue_to_flow_map(packet, flow, tcp_flags,
-                                               flow_map, map_cnt++);
-                }
+                dfc_processing_enqueue_classified_packet(
+                        packet, flow, tcp_flags, batch_enable,
+                        batches, n_batches, flow_map, &map_cnt);
+                continue;
+            }
+        }
+
+        if (!flow && simple_match_enabled) {
+            ovs_be16 dl_type = 0, vlan_tci = 0;
+            uint8_t nw_frag = 0;
+
+            tcp_flags = parse_tcp_flags(packet, &dl_type, &nw_frag, &vlan_tci);
+            flow = dp_netdev_simple_match_lookup(pmd, port_no, dl_type,
+                                                 nw_frag, vlan_tci);
+            if (OVS_LIKELY(flow)) {
+                n_simple_hit++;
+                dfc_processing_enqueue_classified_packet(
+                        packet, flow, tcp_flags, batch_enable,
+                        batches, n_batches, flow_map, &map_cnt);
                 continue;
             }
         }
@@ -7395,17 +7641,9 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
         if (OVS_LIKELY(flow)) {
             tcp_flags = miniflow_get_tcp_flags(&key->mf);
             n_emc_hit++;
-            if (OVS_LIKELY(batch_enable)) {
-                dp_netdev_queue_batches(packet, flow, tcp_flags, batches,
-                                        n_batches);
-            } else {
-                /* Flow batching should be performed only after fast-path
-                 * processing is also completed for packets with emc miss
-                 * or else it will result in reordering of packets with
-                 * same datapath flows. */
-                packet_enqueue_to_flow_map(packet, flow, tcp_flags,
-                                           flow_map, map_cnt++);
-            }
+            dfc_processing_enqueue_classified_packet(
+                    packet, flow, tcp_flags, batch_enable,
+                    batches, n_batches, flow_map, &map_cnt);
         } else {
             /* Exact match cache missed. Group missed packets together at
              * the beginning of the 'packets' array. */
@@ -7433,9 +7671,11 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
     pmd_perf_update_counter(&pmd->perf_stats, PMD_STAT_PHWOL_HIT, n_phwol_hit);
     pmd_perf_update_counter(&pmd->perf_stats, PMD_STAT_MFEX_OPT_HIT,
                             n_mfex_opt_hit);
+    pmd_perf_update_counter(&pmd->perf_stats, PMD_STAT_SIMPLE_HIT,
+                            n_simple_hit);
     pmd_perf_update_counter(&pmd->perf_stats, PMD_STAT_EXACT_HIT, n_emc_hit);
 
-    if (!pmd->ctx.smc_enable_db) {
+    if (!smc_enable_db) {
         return dp_packet_batch_size(packets_);
     }
 
@@ -7484,7 +7724,7 @@ handle_packet_upcall(struct dp_netdev_pmd_thread *pmd,
      * Netlink and struct flow representations, we have to do the same
      * here.  This must be in sync with 'match' in dpif_netdev_flow_put(). */
     if (!match.wc.masks.vlans[0].tci) {
-        match.wc.masks.vlans[0].tci = htons(0xffff);
+        match.wc.masks.vlans[0].tci = htons(VLAN_VID_MASK | VLAN_CFI);
     }
 
     /* We can't allow the packet batching in the next loop to execute
diff --git a/lib/flow.c b/lib/flow.c
index 89837de95..bba60de81 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -1110,22 +1110,29 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
 }
 
 static ovs_be16
-parse_dl_type(const void **datap, size_t *sizep)
+parse_dl_type(const void **datap, size_t *sizep, ovs_be16 *first_vlan_tci_p)
 {
     union flow_vlan_hdr vlans[FLOW_MAX_VLAN_HEADERS];
 
-    parse_vlan(datap, sizep, vlans);
+    if (parse_vlan(datap, sizep, vlans) && first_vlan_tci_p) {
+        *first_vlan_tci_p = vlans[0].tci;
+    }
 
     return parse_ethertype(datap, sizep);
 }
 
 /* Parses and return the TCP flags in 'packet', converted to host byte order.
  * If 'packet' is not an Ethernet packet embedding TCP, returns 0.
+ * 'dl_type_p' will be set only if the 'packet' is an Ethernet packet.
+ * 'nw_frag_p' will be set only if the 'packet' is an IP packet.
+ * 'first_vlan_tci' will be set only if the 'packet' contains vlan header.
  *
  * The caller must ensure that 'packet' is at least ETH_HEADER_LEN bytes
  * long.'*/
 uint16_t
-parse_tcp_flags(struct dp_packet *packet)
+parse_tcp_flags(struct dp_packet *packet,
+                ovs_be16 *dl_type_p, uint8_t *nw_frag_p,
+                ovs_be16 *first_vlan_tci_p)
 {
     const void *data = dp_packet_data(packet);
     const char *frame = (const char *)data;
@@ -1139,7 +1146,10 @@ parse_tcp_flags(struct dp_packet *packet)
 
     dp_packet_reset_offsets(packet);
 
-    dl_type = parse_dl_type(&data, &size);
+    dl_type = parse_dl_type(&data, &size, first_vlan_tci_p);
+    if (dl_type_p) {
+        *dl_type_p = dl_type;
+    }
     if (OVS_UNLIKELY(eth_type_mpls(dl_type))) {
         packet->l2_5_ofs = (char *)data - frame;
     }
@@ -1186,6 +1196,10 @@ parse_tcp_flags(struct dp_packet *packet)
         return 0;
     }
 
+    if (nw_frag_p) {
+        *nw_frag_p = nw_frag;
+    }
+
     packet->l4_ofs = (uint16_t)((char *)data - frame);
     if (!(nw_frag & FLOW_NW_FRAG_LATER) && nw_proto == IPPROTO_TCP &&
         size >= TCP_HEADER_LEN) {
diff --git a/lib/flow.h b/lib/flow.h
index 467b2801d..c647ad83c 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -134,7 +134,8 @@ bool parse_ipv6_ext_hdrs(const void **datap, size_t *sizep, uint8_t *nw_proto,
                          uint8_t *nw_frag,
                          const struct ovs_16aligned_ip6_frag **frag_hdr);
 bool parse_nsh(const void **datap, size_t *sizep, struct ovs_key_nsh *key);
-uint16_t parse_tcp_flags(struct dp_packet *packet);
+uint16_t parse_tcp_flags(struct dp_packet *packet, ovs_be16 *dl_type_p,
+                         uint8_t *nw_frag_p, ovs_be16 *first_vlan_tci_p);
 
 static inline uint64_t
 flow_get_xreg(const struct flow *flow, int idx)
diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c
index f6706ee0c..3a10508d9 100644
--- a/lib/netdev-offload-dpdk.c
+++ b/lib/netdev-offload-dpdk.c
@@ -2180,7 +2180,7 @@ netdev_offload_dpdk_hw_miss_packet_recover(struct netdev *netdev,
             ret = EOPNOTSUPP;
             goto close_vport_netdev;
         }
-        parse_tcp_flags(packet);
+        parse_tcp_flags(packet, NULL, NULL, NULL);
         if (vport_netdev->netdev_class->pop_header(packet) == NULL) {
             /* If there is an error with popping the header, the packet is
              * freed. In this case it should not continue SW processing.
diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
index 53eee185a..b58df0365 100644
--- a/tests/dpif-netdev.at
+++ b/tests/dpif-netdev.at
@@ -192,7 +192,7 @@ skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc
    ovs-appctl revalidator/wait
    # Dump the datapath flow to see that it goes to p2 ("actions:2").
    AT_CHECK([ovs-appctl dpif/dump-flows br0], [0], [dnl
-recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:01:02:03:04:05),eth_type(0x8100),vlan(vid=1000,pcp=5),encap(eth_type(0x0800),ipv4(frag=no)), packets:0, bytes:0, used:never, actions:2
+recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:01:02:03:04:05),eth_type(0x8100),vlan(vid=1000,pcp=5/0x0),encap(eth_type(0x0800),ipv4(frag=no)), packets:0, bytes:0, used:never, actions:2
 ])
 
    # Delete the flows, then add new flows that would not match the same
@@ -210,7 +210,7 @@ recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:
    ovs-appctl revalidator/wait
    # Dump the datapath flow to see that it goes to p1 ("actions:IN_PORT").
    AT_CHECK([ovs-appctl dpif/dump-flows br0 | strip_timers], [0], [dnl
-recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:01:02:03:04:05),eth_type(0x8100),vlan(vid=1000,pcp=5),encap(eth_type(0x0800),ipv4(frag=no)), packets:1, bytes:64, used:0.0s, actions:1
+recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:01:02:03:04:05),eth_type(0x8100),vlan(vid=1000,pcp=5/0x0),encap(eth_type(0x0800),ipv4(frag=no)), packets:1, bytes:64, used:0.0s, actions:1
 ])
    OVS_VSWITCHD_STOP
    AT_CLEANUP])
@@ -428,7 +428,7 @@ skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc
    # Check that flow successfully offloaded.
    OVS_WAIT_UNTIL([grep "succeed to add netdev flow" ovs-vswitchd.log])
    AT_CHECK([filter_hw_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl
-p1: flow put[[create]]: flow match: recirc_id=0,eth,ip,in_port=1,vlan_tci=0x0000,nw_frag=no, mark: 1
+p1: flow put[[create]]: flow match: recirc_id=0,eth,ip,in_port=1,vlan_tci=0x0000/0x1fff,nw_frag=no, mark: 1
 ])
    # Check that datapath flow installed successfully.
    AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl
@@ -439,7 +439,7 @@ recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), a
 
    # Check for succesfull packet matching with installed offloaded flow.
    AT_CHECK([filter_hw_packet_netdev_dummy < ovs-vswitchd.log | strip_xout], [0], [dnl
-p1: packet: ip,vlan_tci=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,nw_src=127.0.0.1,nw_dst=127.0.0.1,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=64 matches with flow: recirc_id=0,eth,ip,vlan_tci=0x0000,nw_frag=no with mark: 1
+p1: packet: ip,vlan_tci=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,nw_src=127.0.0.1,nw_dst=127.0.0.1,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=64 matches with flow: recirc_id=0,eth,ip,vlan_tci=0x0000/0x1fff,nw_frag=no with mark: 1
 ])
 
    ovs-appctl revalidator/wait
@@ -495,11 +495,11 @@ packet_type(ns=0,id=0),eth(src=00:06:07:08:09:0a,dst=00:01:02:03:04:05),eth_type
    # Check that flow successfully offloaded.
    OVS_WAIT_UNTIL([grep "succeed to add netdev flow" ovs-vswitchd.log])
    AT_CHECK([filter_hw_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl
-p1: flow put[[create]]: flow match: recirc_id=0,eth,udp,in_port=1,dl_vlan=99,dl_vlan_pcp=7,nw_src=127.0.0.1,nw_frag=no,tp_dst=82, mark: 1
+p1: flow put[[create]]: flow match: recirc_id=0,eth,udp,in_port=1,dl_vlan=99,nw_src=127.0.0.1,nw_frag=no,tp_dst=82, mark: 1
 ])
    # Check that datapath flow installed successfully.
    AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl
-recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=127.0.0.1,proto=17,frag=no),udp(dst=82)), actions: <del>
+recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=7/0x0),encap(eth_type(0x0800),ipv4(src=127.0.0.1,proto=17,frag=no),udp(dst=82)), actions: <del>
 ])
    # Inject the same packet again.
    AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet --len 64], [0])
@@ -507,13 +507,13 @@ recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=
    # Check for succesfull packet matching with installed offloaded flow.
    AT_CHECK([filter_hw_packet_netdev_dummy < ovs-vswitchd.log | strip_xout], [0], [dnl
 p1: packet: udp,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,nw_src=127.0.0.1,nw_dst=127.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=81,tp_dst=82 dnl
-matches with flow: recirc_id=0,eth,udp,dl_vlan=99,dl_vlan_pcp=7,nw_src=127.0.0.1,nw_frag=no,tp_dst=82 with mark: 1
+matches with flow: recirc_id=0,eth,udp,dl_vlan=99,nw_src=127.0.0.1,nw_frag=no,tp_dst=82 with mark: 1
 ])
 
    ovs-appctl revalidator/wait
    # Dump the datapath flow to see that actions was executed for a packet.
    AT_CHECK([ovs-appctl dpif/dump-flows br0 | strip_timers], [0], [dnl
-recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=127.0.0.1,proto=17,frag=no),udp(dst=82)), dnl
+recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=7/0x0),encap(eth_type(0x0800),ipv4(src=127.0.0.1,proto=17,frag=no),udp(dst=82)), dnl
 packets:1, bytes:64, used:0.0s, actions:set(ipv4(src=192.168.0.7)),set(udp(dst=3773)),1
 ])
 
diff --git a/tests/nsh.at b/tests/nsh.at
index b958be253..4d49f1201 100644
--- a/tests/nsh.at
+++ b/tests/nsh.at
@@ -173,7 +173,7 @@ AT_CHECK([
     ovs-appctl dpctl/dump-flows dummy at ovs-dummy | strip_used | grep -v ipv6 | sort
 ], [0], [flow-dump from the main thread:
 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_vlan(vid=100,pcp=0),push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x0,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),pop_nsh(),recirc(0x4)
-recirc_id(0x4),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=100,pcp=0),encap(eth_type(0x0800),ipv4(frag=no)), packets:1, bytes:102, used:0.0s, actions:2
+recirc_id(0x4),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=100),encap(eth_type(0x0800),ipv4(frag=no)), packets:1, bytes:102, used:0.0s, actions:2
 ])
 
 OVS_VSWITCHD_STOP
diff --git a/tests/pmd.at b/tests/pmd.at
index 225d4ee3a..c728b47af 100644
--- a/tests/pmd.at
+++ b/tests/pmd.at
@@ -380,13 +380,14 @@ dummy at ovs-dummy: hit:0 missed:0
     p0 7/1: (dummy-pmd: configured_rx_queues=4, configured_tx_queues=<cleared>, requested_rx_queues=4, requested_tx_queues=<cleared>)
 ])
 
-AT_CHECK([ovs-appctl dpif-netdev/pmd-stats-show | sed SED_NUMA_CORE_PATTERN | sed '/cycles/d' | grep pmd -A 11], [0], [dnl
+AT_CHECK([ovs-appctl dpif-netdev/pmd-stats-show | sed SED_NUMA_CORE_PATTERN | sed '/cycles/d' | grep pmd -A 12], [0], [dnl
 pmd thread numa_id <cleared> core_id <cleared>:
   packets received: 0
   packet recirculations: 0
   avg. datapath passes per packet: 0.00
   phwol hits: 0
   mfex opt hits: 0
+  simple match hits: 0
   emc hits: 0
   smc hits: 0
   megaflow hits: 0
@@ -413,13 +414,14 @@ AT_CHECK([cat ovs-vswitchd.log | filter_flow_install | strip_xout], [0], [dnl
 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:77,dst=50:54:00:00:01:78),eth_type(0x0800),ipv4(frag=no), actions: <del>
 ])
 
-AT_CHECK([ovs-appctl dpif-netdev/pmd-stats-show | sed SED_NUMA_CORE_PATTERN | sed '/cycles/d' | grep pmd -A 11], [0], [dnl
+AT_CHECK([ovs-appctl dpif-netdev/pmd-stats-show | sed SED_NUMA_CORE_PATTERN | sed '/cycles/d' | grep pmd -A 12], [0], [dnl
 pmd thread numa_id <cleared> core_id <cleared>:
   packets received: 20
   packet recirculations: 0
   avg. datapath passes per packet: 1.00
   phwol hits: 0
   mfex opt hits: 0
+  simple match hits: 0
   emc hits: 19
   smc hits: 0
   megaflow hits: 0
-- 
2.31.1



More information about the dev mailing list