diff mbox series

[ovs-dev,V4,13/14] netdev-offload-dpdk: Support vports flows offload

Message ID 20210317063538.12451-14-elibr@nvidia.com
State Changes Requested
Headers show
Series Netdev vxlan-decap offload | expand

Commit Message

Eli Britstein March 17, 2021, 6:35 a.m. UTC
Vports are virtual, OVS only logical devices, so rte_flows cannot be
applied as is on them. Instead, apply the rules the physical port from
which the packet has arrived, provided by orig_in_port field.

Signed-off-by: Eli Britstein <elibr@nvidia.com>
Reviewed-by: Gaetan Rivet <gaetanr@nvidia.com>
---
 lib/netdev-offload-dpdk.c | 204 ++++++++++++++++++++++++++++++++------
 1 file changed, 171 insertions(+), 33 deletions(-)

Comments

Sriharsha Basavapatna March 30, 2021, 11:25 a.m. UTC | #1
On Wed, Mar 17, 2021 at 12:05 PM Eli Britstein <elibr@nvidia.com> wrote:
>
> Vports are virtual, OVS only logical devices, so rte_flows cannot be
> applied as is on them. Instead, apply the rules the physical port from
> which the packet has arrived, provided by orig_in_port field.
>
> Signed-off-by: Eli Britstein <elibr@nvidia.com>
> Reviewed-by: Gaetan Rivet <gaetanr@nvidia.com>
> ---
>  lib/netdev-offload-dpdk.c | 204 ++++++++++++++++++++++++++++++++------
>  1 file changed, 171 insertions(+), 33 deletions(-)
>
> diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c
> index ade7fae09..69aaefa0f 100644
> --- a/lib/netdev-offload-dpdk.c
> +++ b/lib/netdev-offload-dpdk.c
> @@ -25,6 +25,7 @@
>  #include "netdev-offload-provider.h"
>  #include "netdev-provider.h"
>  #include "netdev-vport.h"
> +#include "odp-util.h"
>  #include "openvswitch/match.h"
>  #include "openvswitch/vlog.h"
>  #include "packets.h"
> @@ -62,6 +63,7 @@ struct ufid_to_rte_flow_data {
>      struct rte_flow *rte_flow;
>      bool actions_offloaded;
>      struct dpif_flow_stats stats;
> +    struct netdev *physdev;
>  };
>
>  /* Find rte_flow with @ufid. */
> @@ -87,7 +89,8 @@ ufid_to_rte_flow_data_find(const ovs_u128 *ufid, bool warn)
>
>  static inline struct ufid_to_rte_flow_data *
>  ufid_to_rte_flow_associate(const ovs_u128 *ufid, struct netdev *netdev,
> -                           struct rte_flow *rte_flow, bool actions_offloaded)
> +                           struct netdev *vport, struct rte_flow *rte_flow,
> +                           bool actions_offloaded)
>  {
>      size_t hash = hash_bytes(ufid, sizeof *ufid, 0);
>      struct ufid_to_rte_flow_data *data = xzalloc(sizeof *data);
> @@ -105,7 +108,8 @@ ufid_to_rte_flow_associate(const ovs_u128 *ufid, struct netdev *netdev,
>      }
>
>      data->ufid = *ufid;
> -    data->netdev = netdev_ref(netdev);
> +    data->physdev = netdev_ref(netdev);
> +    data->netdev = vport ? netdev_ref(vport) : netdev;
>      data->rte_flow = rte_flow;
>      data->actions_offloaded = actions_offloaded;
>
> @@ -121,7 +125,10 @@ ufid_to_rte_flow_disassociate(struct ufid_to_rte_flow_data *data)
>
>      cmap_remove(&ufid_to_rte_flow,
>                  CONST_CAST(struct cmap_node *, &data->node), hash);
> -    netdev_close(data->netdev);
> +    if (data->netdev != data->physdev) {
> +        netdev_close(data->netdev);
> +    }
> +    netdev_close(data->physdev);
>      ovsrcu_postpone(free, data);
>  }
>
> @@ -134,6 +141,8 @@ struct flow_patterns {
>      struct rte_flow_item *items;
>      int cnt;
>      int current_max;
> +    uint32_t tnl_pmd_items_cnt;
> +    struct ds s_tnl;
>  };
>
>  struct flow_actions {
> @@ -154,16 +163,20 @@ struct flow_actions {
>  static void
>  dump_flow_attr(struct ds *s, struct ds *s_extra,
>                 const struct rte_flow_attr *attr,
> +               struct flow_patterns *flow_patterns,
>                 struct flow_actions *flow_actions)
>  {
>      if (flow_actions->tnl_pmd_actions_cnt) {
>          ds_clone(s_extra, &flow_actions->s_tnl);
> +    } else if (flow_patterns->tnl_pmd_items_cnt) {
> +        ds_clone(s_extra, &flow_patterns->s_tnl);
>      }
> -    ds_put_format(s, "%s%spriority %"PRIu32" group %"PRIu32" %s%s",
> +    ds_put_format(s, "%s%spriority %"PRIu32" group %"PRIu32" %s%s%s",
>                    attr->ingress  ? "ingress " : "",
>                    attr->egress   ? "egress " : "", attr->priority, attr->group,
>                    attr->transfer ? "transfer " : "",
> -                  flow_actions->tnl_pmd_actions_cnt ? "tunnel_set 1 " : "");
> +                  flow_actions->tnl_pmd_actions_cnt ? "tunnel_set 1 " : "",
> +                  flow_patterns->tnl_pmd_items_cnt ? "tunnel_match 1 " : "");
>  }
>
>  /* Adds one pattern item 'field' with the 'mask' to dynamic string 's' using
> @@ -177,9 +190,18 @@ dump_flow_attr(struct ds *s, struct ds *s_extra,
>      }
>
>  static void
> -dump_flow_pattern(struct ds *s, const struct rte_flow_item *item)
> +dump_flow_pattern(struct ds *s,
> +                  struct flow_patterns *flow_patterns,
> +                  int pattern_index)
>  {
> -    if (item->type == RTE_FLOW_ITEM_TYPE_ETH) {
> +    const struct rte_flow_item *item = &flow_patterns->items[pattern_index];
> +
> +    if (item->type == RTE_FLOW_ITEM_TYPE_END) {
> +        ds_put_cstr(s, "end ");
> +    } else if (flow_patterns->tnl_pmd_items_cnt &&
> +               pattern_index < flow_patterns->tnl_pmd_items_cnt) {
> +        return;
> +    } else if (item->type == RTE_FLOW_ITEM_TYPE_ETH) {
>          const struct rte_flow_item_eth *eth_spec = item->spec;
>          const struct rte_flow_item_eth *eth_mask = item->mask;
>
> @@ -569,19 +591,19 @@ dump_flow_action(struct ds *s, struct ds *s_extra,
>  static struct ds *
>  dump_flow(struct ds *s, struct ds *s_extra,
>            const struct rte_flow_attr *attr,
> -          const struct rte_flow_item *items,
> +          struct flow_patterns *flow_patterns,
>            struct flow_actions *flow_actions)
>  {
>      int i;
>
>      if (attr) {
> -        dump_flow_attr(s, s_extra, attr, flow_actions);
> +        dump_flow_attr(s, s_extra, attr, flow_patterns, flow_actions);
>      }
>      ds_put_cstr(s, "pattern ");
> -    while (items && items->type != RTE_FLOW_ITEM_TYPE_END) {
> -        dump_flow_pattern(s, items++);
> +    for (i = 0; i < flow_patterns->cnt; i++) {
> +        dump_flow_pattern(s, flow_patterns, i);
>      }
> -    ds_put_cstr(s, "end actions ");
> +    ds_put_cstr(s, "actions ");
>      for (i = 0; i < flow_actions->cnt; i++) {
>          dump_flow_action(s, s_extra, flow_actions, i);
>      }
> @@ -591,11 +613,12 @@ dump_flow(struct ds *s, struct ds *s_extra,
>  static struct rte_flow *
>  netdev_offload_dpdk_flow_create(struct netdev *netdev,
>                                  const struct rte_flow_attr *attr,
> -                                const struct rte_flow_item *items,
> +                                struct flow_patterns *flow_patterns,
>                                  struct flow_actions *flow_actions,
>                                  struct rte_flow_error *error)
>  {
>      const struct rte_flow_action *actions = flow_actions->actions;
> +    const struct rte_flow_item *items = flow_patterns->items;
>      struct ds s_extra = DS_EMPTY_INITIALIZER;
>      struct ds s = DS_EMPTY_INITIALIZER;
>      struct rte_flow *flow;
> @@ -604,7 +627,7 @@ netdev_offload_dpdk_flow_create(struct netdev *netdev,
>      flow = netdev_dpdk_rte_flow_create(netdev, attr, items, actions, error);
>      if (flow) {
>          if (!VLOG_DROP_DBG(&rl)) {
> -            dump_flow(&s, &s_extra, attr, items, flow_actions);
> +            dump_flow(&s, &s_extra, attr, flow_patterns, flow_actions);
>              extra_str = ds_cstr(&s_extra);
>              VLOG_DBG_RL(&rl, "%s: rte_flow 0x%"PRIxPTR" %s  flow create %d %s",
>                          netdev_get_name(netdev), (intptr_t) flow, extra_str,
> @@ -619,7 +642,7 @@ netdev_offload_dpdk_flow_create(struct netdev *netdev,
>          VLOG_RL(&rl, level, "%s: rte_flow creation failed: %d (%s).",
>                  netdev_get_name(netdev), error->type, error->message);
>          if (!vlog_should_drop(&this_module, level, &rl)) {
> -            dump_flow(&s, &s_extra, attr, items, flow_actions);
> +            dump_flow(&s, &s_extra, attr, flow_patterns, flow_actions);
>              extra_str = ds_cstr(&s_extra);
>              VLOG_RL(&rl, level, "%s: Failed flow: %s  flow create %d %s",
>                      netdev_get_name(netdev), extra_str,
> @@ -654,6 +677,28 @@ add_flow_pattern(struct flow_patterns *patterns, enum rte_flow_item_type type,
>      patterns->cnt++;
>  }
>
> +static void
> +add_flow_tnl_patterns(struct flow_patterns *all_patterns,
> +                      struct rte_flow_item *tnl_pmd_items,
> +                      uint32_t tnl_pmd_items_cnt,
> +                      struct flow_patterns *flow_patterns)
> +{
> +    int i;
> +
> +    all_patterns->tnl_pmd_items_cnt = tnl_pmd_items_cnt;
> +
> +    for (i = 0; i < tnl_pmd_items_cnt; i++) {
> +        add_flow_pattern(all_patterns, tnl_pmd_items[i].type,
> +                         tnl_pmd_items[i].spec, tnl_pmd_items[i].mask);
> +    }
> +
> +    for (i = 0; i < flow_patterns->cnt; i++) {
> +        add_flow_pattern(all_patterns, flow_patterns->items[i].type,
> +                         flow_patterns->items[i].spec,
> +                         flow_patterns->items[i].mask);
> +    }
> +}
> +
>  static void
>  add_flow_action(struct flow_actions *actions, enum rte_flow_action_type type,
>                  const void *conf)
> @@ -1497,6 +1542,7 @@ parse_flow_actions(struct netdev *netdev,
>
>  static struct ufid_to_rte_flow_data *
>  create_netdev_offload(struct netdev *netdev,
> +                      struct netdev *vport,
>                        const ovs_u128 *ufid,
>                        struct flow_patterns *flow_patterns,
>                        struct flow_actions *flow_actions,
> @@ -1505,33 +1551,35 @@ create_netdev_offload(struct netdev *netdev,
>  {
>      struct rte_flow_attr flow_attr = { .ingress = 1, .transfer = 1, };
>      struct flow_actions rss_actions = { .actions = NULL, .cnt = 0 };
> -    struct rte_flow_item *items = flow_patterns->items;
>      struct ufid_to_rte_flow_data *flow_data = NULL;
>      bool actions_offloaded = true;
>      struct rte_flow *flow = NULL;
>      struct rte_flow_error error;
>
>      if (enable_full) {
> -        flow = netdev_offload_dpdk_flow_create(netdev, &flow_attr, items,
> -                                               flow_actions, &error);
> +        flow = netdev_offload_dpdk_flow_create(netdev, &flow_attr,
> +                                               flow_patterns, flow_actions,
> +                                               &error);
>      }
>
> -    if (!flow) {
> +    if (!vport && !flow) {
>          /* If we failed to offload the rule actions fallback to MARK+RSS
>           * actions.
>           */
>          actions_offloaded = false;
>          flow_attr.transfer = 0;
>          add_flow_mark_rss_actions(&rss_actions, flow_mark, netdev);
> -        flow = netdev_offload_dpdk_flow_create(netdev, &flow_attr, items,
> -                                               &rss_actions, &error);
> +        flow = netdev_offload_dpdk_flow_create(netdev, &flow_attr,
> +                                               flow_patterns, &rss_actions,
> +                                               &error);
>          free_flow_actions(&rss_actions);
>      }
>
>      if (flow) {
> -        flow_data = ufid_to_rte_flow_associate(ufid, netdev, flow,
> +        flow_data = ufid_to_rte_flow_associate(ufid, netdev, vport, flow,
>                                                 actions_offloaded);
> -        VLOG_DBG("%s: installed flow %p by ufid "UUID_FMT,
> +        VLOG_DBG("%s/%s: installed flow %p by ufid "UUID_FMT,
> +                 vport ? netdev_get_name(vport) : netdev_get_name(netdev),
>                   netdev_get_name(netdev), flow,
>                   UUID_ARGS((struct uuid *) ufid));
>      }
> @@ -1539,6 +1587,55 @@ create_netdev_offload(struct netdev *netdev,
>      return flow_data;
>  }
>
> +static struct ufid_to_rte_flow_data *
> +create_vport_offload(struct netdev *vport,
> +                     odp_port_t orig_in_port,
> +                     const ovs_u128 *ufid,
> +                     struct flow_patterns *flow_patterns,
> +                     struct flow_actions *flow_actions)
> +{
> +    struct flow_patterns all_patterns = { .items = NULL, .cnt = 0 };
> +    struct ufid_to_rte_flow_data *flows_data = NULL;
> +    struct rte_flow_item *tnl_pmd_items;
> +    struct rte_flow_tunnel tunnel;
> +    struct rte_flow_error error;
> +    uint32_t tnl_pmd_items_cnt;
> +    struct netdev *physdev;
> +
> +    physdev = netdev_ports_get(orig_in_port, vport->dpif_type);
> +    if (physdev == NULL) {
> +        return NULL;
> +    }
> +
> +    if (vport_to_rte_tunnel(vport, &tunnel, physdev,
> +                            &all_patterns.s_tnl)) {
> +        goto out;
> +    }
> +    if (netdev_dpdk_rte_flow_tunnel_match(physdev, &tunnel, &tnl_pmd_items,
> +                                          &tnl_pmd_items_cnt, &error)) {
> +        VLOG_DBG_RL(&rl, "%s: netdev_dpdk_rte_flow_tunnel_match failed: "
> +                    "%d (%s).", netdev_get_name(physdev), error.type,
> +                    error.message);
> +        goto out;
> +    }
> +    add_flow_tnl_patterns(&all_patterns, tnl_pmd_items, tnl_pmd_items_cnt,
> +                          flow_patterns);
> +    flows_data = create_netdev_offload(physdev, vport, ufid, &all_patterns,
> +                                       flow_actions, true, 0);
> +    if (netdev_dpdk_rte_flow_tunnel_item_release(physdev, tnl_pmd_items,
> +                                                 tnl_pmd_items_cnt,
> +                                                 &error)) {

Call tunnel_item_release() only if tnl_pmd_items_cnt is non-zero and
handle this in free_flow_patterns() ? like
tunnel_action_decap_release() is handled in free_flow_actions().



> +        VLOG_DBG_RL(&rl, "%s: netdev_dpdk_rte_flow_tunnel_item_release "
> +                    "failed: %d (%s).", netdev_get_name(physdev),
> +                    error.type, error.message);
> +    }
> +out:
> +    all_patterns.cnt = 0;
> +    free_flow_patterns(&all_patterns);
> +    netdev_close(physdev);
> +    return flows_data;
> +}
> +
>  static struct ufid_to_rte_flow_data *
>  netdev_offload_dpdk_add_flow(struct netdev *netdev,
>                               struct match *match,
> @@ -1560,8 +1657,14 @@ netdev_offload_dpdk_add_flow(struct netdev *netdev,
>
>      err = parse_flow_actions(netdev, &actions, nl_actions, actions_len);
>
> -    flow_data = create_netdev_offload(netdev, ufid, &patterns, &actions, !err,
> -                                      info->flow_mark);
> +    if (netdev_vport_is_vport_class(netdev->netdev_class)) {
> +        flow_data = err ? NULL :
> +            create_vport_offload(netdev, info->orig_in_port, ufid, &patterns,
> +                                 &actions);
> +    } else {
> +        flow_data = create_netdev_offload(netdev, NULL, ufid, &patterns,
> +                                          &actions, !err, info->flow_mark);
> +    }
>
>  out:
>      free_flow_patterns(&patterns);
> @@ -1574,32 +1677,55 @@ netdev_offload_dpdk_flow_destroy(struct ufid_to_rte_flow_data *rte_flow_data)
>  {
>      struct rte_flow_error error;
>      struct rte_flow *rte_flow;
> +    struct netdev *physdev;
>      struct netdev *netdev;
>      ovs_u128 *ufid;
>      int ret;
>
>      rte_flow = rte_flow_data->rte_flow;
> +    physdev = rte_flow_data->physdev;
>      netdev = rte_flow_data->netdev;
>      ufid = &rte_flow_data->ufid;
>
> -    ret = netdev_dpdk_rte_flow_destroy(netdev, rte_flow, &error);
> +    ret = netdev_dpdk_rte_flow_destroy(physdev, rte_flow, &error);
>
>      if (ret == 0) {
>          ufid_to_rte_flow_disassociate(rte_flow_data);
> -        VLOG_DBG_RL(&rl, "%s: rte_flow 0x%"PRIxPTR
> +        VLOG_DBG_RL(&rl, "%s/%s: rte_flow 0x%"PRIxPTR
>                      " flow destroy %d ufid " UUID_FMT,
> -                    netdev_get_name(netdev), (intptr_t) rte_flow,
> +                    netdev_get_name(netdev), netdev_get_name(physdev),
> +                    (intptr_t) rte_flow,
>                      netdev_dpdk_get_port_id(netdev),
>                      UUID_ARGS((struct uuid *) ufid));
>      } else {
> -        VLOG_ERR("Failed flow: %s: flow destroy %d ufid " UUID_FMT,
> -                 netdev_get_name(netdev), netdev_dpdk_get_port_id(netdev),
> +        VLOG_ERR("Failed flow: %s/%s: flow destroy %d ufid " UUID_FMT,
> +                 netdev_get_name(netdev), netdev_get_name(physdev),
> +                 netdev_dpdk_get_port_id(netdev),
>                   UUID_ARGS((struct uuid *) ufid));
>      }
>
>      return ret;
>  }
>
> +struct get_netdev_odp_aux {
> +    struct netdev *netdev;
> +    odp_port_t odp_port;
> +};
> +
> +static bool
> +get_netdev_odp_cb(struct netdev *netdev,
> +                  odp_port_t odp_port,
> +                  void *aux_)
> +{
> +    struct get_netdev_odp_aux *aux = aux_;
> +
> +    if (netdev == aux->netdev) {
> +        aux->odp_port = odp_port;
> +        return true;
> +    }
> +    return false;
> +}
> +
>  static int
>  netdev_offload_dpdk_flow_put(struct netdev *netdev, struct match *match,
>                               struct nlattr *actions, size_t actions_len,
> @@ -1618,6 +1744,17 @@ netdev_offload_dpdk_flow_put(struct netdev *netdev, struct match *match,
>       */
>      rte_flow_data = ufid_to_rte_flow_data_find(ufid, false);
>      if (rte_flow_data && rte_flow_data->rte_flow) {
> +        struct get_netdev_odp_aux aux = {
> +            .netdev = rte_flow_data->physdev,
> +            .odp_port = ODPP_NONE,
> +        };
> +
> +        /* Extract the orig_in_port from physdev as in case of modify the one
> +         * provided by upper layer cannot be used.
> +         */
> +        netdev_ports_traverse(rte_flow_data->physdev->dpif_type,
> +                              get_netdev_odp_cb, &aux);
> +        info->orig_in_port = aux.odp_port;
>          old_stats = rte_flow_data->stats;
>          modification = true;
>          ret = netdev_offload_dpdk_flow_destroy(rte_flow_data);
> @@ -1698,8 +1835,9 @@ netdev_offload_dpdk_flow_get(struct netdev *netdev,
>          goto out;
>      }
>      attrs->dp_layer = "dpdk";
> -    ret = netdev_dpdk_rte_flow_query_count(netdev, rte_flow_data->rte_flow,
> -                                           &query, &error);
> +    ret = netdev_dpdk_rte_flow_query_count(rte_flow_data->physdev,
> +                                           rte_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),
> @@ -1723,7 +1861,7 @@ netdev_offload_dpdk_flow_flush(struct netdev *netdev)
>      struct ufid_to_rte_flow_data *data;
>
>      CMAP_FOR_EACH (data, node, &ufid_to_rte_flow) {
> -        if (data->netdev != netdev) {
> +        if (data->netdev != netdev && data->physdev != netdev) {
>              continue;
>          }
>
> --
> 2.28.0.2311.g225365fb51
>
diff mbox series

Patch

diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c
index ade7fae09..69aaefa0f 100644
--- a/lib/netdev-offload-dpdk.c
+++ b/lib/netdev-offload-dpdk.c
@@ -25,6 +25,7 @@ 
 #include "netdev-offload-provider.h"
 #include "netdev-provider.h"
 #include "netdev-vport.h"
+#include "odp-util.h"
 #include "openvswitch/match.h"
 #include "openvswitch/vlog.h"
 #include "packets.h"
@@ -62,6 +63,7 @@  struct ufid_to_rte_flow_data {
     struct rte_flow *rte_flow;
     bool actions_offloaded;
     struct dpif_flow_stats stats;
+    struct netdev *physdev;
 };
 
 /* Find rte_flow with @ufid. */
@@ -87,7 +89,8 @@  ufid_to_rte_flow_data_find(const ovs_u128 *ufid, bool warn)
 
 static inline struct ufid_to_rte_flow_data *
 ufid_to_rte_flow_associate(const ovs_u128 *ufid, struct netdev *netdev,
-                           struct rte_flow *rte_flow, bool actions_offloaded)
+                           struct netdev *vport, struct rte_flow *rte_flow,
+                           bool actions_offloaded)
 {
     size_t hash = hash_bytes(ufid, sizeof *ufid, 0);
     struct ufid_to_rte_flow_data *data = xzalloc(sizeof *data);
@@ -105,7 +108,8 @@  ufid_to_rte_flow_associate(const ovs_u128 *ufid, struct netdev *netdev,
     }
 
     data->ufid = *ufid;
-    data->netdev = netdev_ref(netdev);
+    data->physdev = netdev_ref(netdev);
+    data->netdev = vport ? netdev_ref(vport) : netdev;
     data->rte_flow = rte_flow;
     data->actions_offloaded = actions_offloaded;
 
@@ -121,7 +125,10 @@  ufid_to_rte_flow_disassociate(struct ufid_to_rte_flow_data *data)
 
     cmap_remove(&ufid_to_rte_flow,
                 CONST_CAST(struct cmap_node *, &data->node), hash);
-    netdev_close(data->netdev);
+    if (data->netdev != data->physdev) {
+        netdev_close(data->netdev);
+    }
+    netdev_close(data->physdev);
     ovsrcu_postpone(free, data);
 }
 
@@ -134,6 +141,8 @@  struct flow_patterns {
     struct rte_flow_item *items;
     int cnt;
     int current_max;
+    uint32_t tnl_pmd_items_cnt;
+    struct ds s_tnl;
 };
 
 struct flow_actions {
@@ -154,16 +163,20 @@  struct flow_actions {
 static void
 dump_flow_attr(struct ds *s, struct ds *s_extra,
                const struct rte_flow_attr *attr,
+               struct flow_patterns *flow_patterns,
                struct flow_actions *flow_actions)
 {
     if (flow_actions->tnl_pmd_actions_cnt) {
         ds_clone(s_extra, &flow_actions->s_tnl);
+    } else if (flow_patterns->tnl_pmd_items_cnt) {
+        ds_clone(s_extra, &flow_patterns->s_tnl);
     }
-    ds_put_format(s, "%s%spriority %"PRIu32" group %"PRIu32" %s%s",
+    ds_put_format(s, "%s%spriority %"PRIu32" group %"PRIu32" %s%s%s",
                   attr->ingress  ? "ingress " : "",
                   attr->egress   ? "egress " : "", attr->priority, attr->group,
                   attr->transfer ? "transfer " : "",
-                  flow_actions->tnl_pmd_actions_cnt ? "tunnel_set 1 " : "");
+                  flow_actions->tnl_pmd_actions_cnt ? "tunnel_set 1 " : "",
+                  flow_patterns->tnl_pmd_items_cnt ? "tunnel_match 1 " : "");
 }
 
 /* Adds one pattern item 'field' with the 'mask' to dynamic string 's' using
@@ -177,9 +190,18 @@  dump_flow_attr(struct ds *s, struct ds *s_extra,
     }
 
 static void
-dump_flow_pattern(struct ds *s, const struct rte_flow_item *item)
+dump_flow_pattern(struct ds *s,
+                  struct flow_patterns *flow_patterns,
+                  int pattern_index)
 {
-    if (item->type == RTE_FLOW_ITEM_TYPE_ETH) {
+    const struct rte_flow_item *item = &flow_patterns->items[pattern_index];
+
+    if (item->type == RTE_FLOW_ITEM_TYPE_END) {
+        ds_put_cstr(s, "end ");
+    } else if (flow_patterns->tnl_pmd_items_cnt &&
+               pattern_index < flow_patterns->tnl_pmd_items_cnt) {
+        return;
+    } else if (item->type == RTE_FLOW_ITEM_TYPE_ETH) {
         const struct rte_flow_item_eth *eth_spec = item->spec;
         const struct rte_flow_item_eth *eth_mask = item->mask;
 
@@ -569,19 +591,19 @@  dump_flow_action(struct ds *s, struct ds *s_extra,
 static struct ds *
 dump_flow(struct ds *s, struct ds *s_extra,
           const struct rte_flow_attr *attr,
-          const struct rte_flow_item *items,
+          struct flow_patterns *flow_patterns,
           struct flow_actions *flow_actions)
 {
     int i;
 
     if (attr) {
-        dump_flow_attr(s, s_extra, attr, flow_actions);
+        dump_flow_attr(s, s_extra, attr, flow_patterns, flow_actions);
     }
     ds_put_cstr(s, "pattern ");
-    while (items && items->type != RTE_FLOW_ITEM_TYPE_END) {
-        dump_flow_pattern(s, items++);
+    for (i = 0; i < flow_patterns->cnt; i++) {
+        dump_flow_pattern(s, flow_patterns, i);
     }
-    ds_put_cstr(s, "end actions ");
+    ds_put_cstr(s, "actions ");
     for (i = 0; i < flow_actions->cnt; i++) {
         dump_flow_action(s, s_extra, flow_actions, i);
     }
@@ -591,11 +613,12 @@  dump_flow(struct ds *s, struct ds *s_extra,
 static struct rte_flow *
 netdev_offload_dpdk_flow_create(struct netdev *netdev,
                                 const struct rte_flow_attr *attr,
-                                const struct rte_flow_item *items,
+                                struct flow_patterns *flow_patterns,
                                 struct flow_actions *flow_actions,
                                 struct rte_flow_error *error)
 {
     const struct rte_flow_action *actions = flow_actions->actions;
+    const struct rte_flow_item *items = flow_patterns->items;
     struct ds s_extra = DS_EMPTY_INITIALIZER;
     struct ds s = DS_EMPTY_INITIALIZER;
     struct rte_flow *flow;
@@ -604,7 +627,7 @@  netdev_offload_dpdk_flow_create(struct netdev *netdev,
     flow = netdev_dpdk_rte_flow_create(netdev, attr, items, actions, error);
     if (flow) {
         if (!VLOG_DROP_DBG(&rl)) {
-            dump_flow(&s, &s_extra, attr, items, flow_actions);
+            dump_flow(&s, &s_extra, attr, flow_patterns, flow_actions);
             extra_str = ds_cstr(&s_extra);
             VLOG_DBG_RL(&rl, "%s: rte_flow 0x%"PRIxPTR" %s  flow create %d %s",
                         netdev_get_name(netdev), (intptr_t) flow, extra_str,
@@ -619,7 +642,7 @@  netdev_offload_dpdk_flow_create(struct netdev *netdev,
         VLOG_RL(&rl, level, "%s: rte_flow creation failed: %d (%s).",
                 netdev_get_name(netdev), error->type, error->message);
         if (!vlog_should_drop(&this_module, level, &rl)) {
-            dump_flow(&s, &s_extra, attr, items, flow_actions);
+            dump_flow(&s, &s_extra, attr, flow_patterns, flow_actions);
             extra_str = ds_cstr(&s_extra);
             VLOG_RL(&rl, level, "%s: Failed flow: %s  flow create %d %s",
                     netdev_get_name(netdev), extra_str,
@@ -654,6 +677,28 @@  add_flow_pattern(struct flow_patterns *patterns, enum rte_flow_item_type type,
     patterns->cnt++;
 }
 
+static void
+add_flow_tnl_patterns(struct flow_patterns *all_patterns,
+                      struct rte_flow_item *tnl_pmd_items,
+                      uint32_t tnl_pmd_items_cnt,
+                      struct flow_patterns *flow_patterns)
+{
+    int i;
+
+    all_patterns->tnl_pmd_items_cnt = tnl_pmd_items_cnt;
+
+    for (i = 0; i < tnl_pmd_items_cnt; i++) {
+        add_flow_pattern(all_patterns, tnl_pmd_items[i].type,
+                         tnl_pmd_items[i].spec, tnl_pmd_items[i].mask);
+    }
+
+    for (i = 0; i < flow_patterns->cnt; i++) {
+        add_flow_pattern(all_patterns, flow_patterns->items[i].type,
+                         flow_patterns->items[i].spec,
+                         flow_patterns->items[i].mask);
+    }
+}
+
 static void
 add_flow_action(struct flow_actions *actions, enum rte_flow_action_type type,
                 const void *conf)
@@ -1497,6 +1542,7 @@  parse_flow_actions(struct netdev *netdev,
 
 static struct ufid_to_rte_flow_data *
 create_netdev_offload(struct netdev *netdev,
+                      struct netdev *vport,
                       const ovs_u128 *ufid,
                       struct flow_patterns *flow_patterns,
                       struct flow_actions *flow_actions,
@@ -1505,33 +1551,35 @@  create_netdev_offload(struct netdev *netdev,
 {
     struct rte_flow_attr flow_attr = { .ingress = 1, .transfer = 1, };
     struct flow_actions rss_actions = { .actions = NULL, .cnt = 0 };
-    struct rte_flow_item *items = flow_patterns->items;
     struct ufid_to_rte_flow_data *flow_data = NULL;
     bool actions_offloaded = true;
     struct rte_flow *flow = NULL;
     struct rte_flow_error error;
 
     if (enable_full) {
-        flow = netdev_offload_dpdk_flow_create(netdev, &flow_attr, items,
-                                               flow_actions, &error);
+        flow = netdev_offload_dpdk_flow_create(netdev, &flow_attr,
+                                               flow_patterns, flow_actions,
+                                               &error);
     }
 
-    if (!flow) {
+    if (!vport && !flow) {
         /* If we failed to offload the rule actions fallback to MARK+RSS
          * actions.
          */
         actions_offloaded = false;
         flow_attr.transfer = 0;
         add_flow_mark_rss_actions(&rss_actions, flow_mark, netdev);
-        flow = netdev_offload_dpdk_flow_create(netdev, &flow_attr, items,
-                                               &rss_actions, &error);
+        flow = netdev_offload_dpdk_flow_create(netdev, &flow_attr,
+                                               flow_patterns, &rss_actions,
+                                               &error);
         free_flow_actions(&rss_actions);
     }
 
     if (flow) {
-        flow_data = ufid_to_rte_flow_associate(ufid, netdev, flow,
+        flow_data = ufid_to_rte_flow_associate(ufid, netdev, vport, flow,
                                                actions_offloaded);
-        VLOG_DBG("%s: installed flow %p by ufid "UUID_FMT,
+        VLOG_DBG("%s/%s: installed flow %p by ufid "UUID_FMT,
+                 vport ? netdev_get_name(vport) : netdev_get_name(netdev),
                  netdev_get_name(netdev), flow,
                  UUID_ARGS((struct uuid *) ufid));
     }
@@ -1539,6 +1587,55 @@  create_netdev_offload(struct netdev *netdev,
     return flow_data;
 }
 
+static struct ufid_to_rte_flow_data *
+create_vport_offload(struct netdev *vport,
+                     odp_port_t orig_in_port,
+                     const ovs_u128 *ufid,
+                     struct flow_patterns *flow_patterns,
+                     struct flow_actions *flow_actions)
+{
+    struct flow_patterns all_patterns = { .items = NULL, .cnt = 0 };
+    struct ufid_to_rte_flow_data *flows_data = NULL;
+    struct rte_flow_item *tnl_pmd_items;
+    struct rte_flow_tunnel tunnel;
+    struct rte_flow_error error;
+    uint32_t tnl_pmd_items_cnt;
+    struct netdev *physdev;
+
+    physdev = netdev_ports_get(orig_in_port, vport->dpif_type);
+    if (physdev == NULL) {
+        return NULL;
+    }
+
+    if (vport_to_rte_tunnel(vport, &tunnel, physdev,
+                            &all_patterns.s_tnl)) {
+        goto out;
+    }
+    if (netdev_dpdk_rte_flow_tunnel_match(physdev, &tunnel, &tnl_pmd_items,
+                                          &tnl_pmd_items_cnt, &error)) {
+        VLOG_DBG_RL(&rl, "%s: netdev_dpdk_rte_flow_tunnel_match failed: "
+                    "%d (%s).", netdev_get_name(physdev), error.type,
+                    error.message);
+        goto out;
+    }
+    add_flow_tnl_patterns(&all_patterns, tnl_pmd_items, tnl_pmd_items_cnt,
+                          flow_patterns);
+    flows_data = create_netdev_offload(physdev, vport, ufid, &all_patterns,
+                                       flow_actions, true, 0);
+    if (netdev_dpdk_rte_flow_tunnel_item_release(physdev, tnl_pmd_items,
+                                                 tnl_pmd_items_cnt,
+                                                 &error)) {
+        VLOG_DBG_RL(&rl, "%s: netdev_dpdk_rte_flow_tunnel_item_release "
+                    "failed: %d (%s).", netdev_get_name(physdev),
+                    error.type, error.message);
+    }
+out:
+    all_patterns.cnt = 0;
+    free_flow_patterns(&all_patterns);
+    netdev_close(physdev);
+    return flows_data;
+}
+
 static struct ufid_to_rte_flow_data *
 netdev_offload_dpdk_add_flow(struct netdev *netdev,
                              struct match *match,
@@ -1560,8 +1657,14 @@  netdev_offload_dpdk_add_flow(struct netdev *netdev,
 
     err = parse_flow_actions(netdev, &actions, nl_actions, actions_len);
 
-    flow_data = create_netdev_offload(netdev, ufid, &patterns, &actions, !err,
-                                      info->flow_mark);
+    if (netdev_vport_is_vport_class(netdev->netdev_class)) {
+        flow_data = err ? NULL :
+            create_vport_offload(netdev, info->orig_in_port, ufid, &patterns,
+                                 &actions);
+    } else {
+        flow_data = create_netdev_offload(netdev, NULL, ufid, &patterns,
+                                          &actions, !err, info->flow_mark);
+    }
 
 out:
     free_flow_patterns(&patterns);
@@ -1574,32 +1677,55 @@  netdev_offload_dpdk_flow_destroy(struct ufid_to_rte_flow_data *rte_flow_data)
 {
     struct rte_flow_error error;
     struct rte_flow *rte_flow;
+    struct netdev *physdev;
     struct netdev *netdev;
     ovs_u128 *ufid;
     int ret;
 
     rte_flow = rte_flow_data->rte_flow;
+    physdev = rte_flow_data->physdev;
     netdev = rte_flow_data->netdev;
     ufid = &rte_flow_data->ufid;
 
-    ret = netdev_dpdk_rte_flow_destroy(netdev, rte_flow, &error);
+    ret = netdev_dpdk_rte_flow_destroy(physdev, rte_flow, &error);
 
     if (ret == 0) {
         ufid_to_rte_flow_disassociate(rte_flow_data);
-        VLOG_DBG_RL(&rl, "%s: rte_flow 0x%"PRIxPTR
+        VLOG_DBG_RL(&rl, "%s/%s: rte_flow 0x%"PRIxPTR
                     " flow destroy %d ufid " UUID_FMT,
-                    netdev_get_name(netdev), (intptr_t) rte_flow,
+                    netdev_get_name(netdev), netdev_get_name(physdev),
+                    (intptr_t) rte_flow,
                     netdev_dpdk_get_port_id(netdev),
                     UUID_ARGS((struct uuid *) ufid));
     } else {
-        VLOG_ERR("Failed flow: %s: flow destroy %d ufid " UUID_FMT,
-                 netdev_get_name(netdev), netdev_dpdk_get_port_id(netdev),
+        VLOG_ERR("Failed flow: %s/%s: flow destroy %d ufid " UUID_FMT,
+                 netdev_get_name(netdev), netdev_get_name(physdev),
+                 netdev_dpdk_get_port_id(netdev),
                  UUID_ARGS((struct uuid *) ufid));
     }
 
     return ret;
 }
 
+struct get_netdev_odp_aux {
+    struct netdev *netdev;
+    odp_port_t odp_port;
+};
+
+static bool
+get_netdev_odp_cb(struct netdev *netdev,
+                  odp_port_t odp_port,
+                  void *aux_)
+{
+    struct get_netdev_odp_aux *aux = aux_;
+
+    if (netdev == aux->netdev) {
+        aux->odp_port = odp_port;
+        return true;
+    }
+    return false;
+}
+
 static int
 netdev_offload_dpdk_flow_put(struct netdev *netdev, struct match *match,
                              struct nlattr *actions, size_t actions_len,
@@ -1618,6 +1744,17 @@  netdev_offload_dpdk_flow_put(struct netdev *netdev, struct match *match,
      */
     rte_flow_data = ufid_to_rte_flow_data_find(ufid, false);
     if (rte_flow_data && rte_flow_data->rte_flow) {
+        struct get_netdev_odp_aux aux = {
+            .netdev = rte_flow_data->physdev,
+            .odp_port = ODPP_NONE,
+        };
+
+        /* Extract the orig_in_port from physdev as in case of modify the one
+         * provided by upper layer cannot be used.
+         */
+        netdev_ports_traverse(rte_flow_data->physdev->dpif_type,
+                              get_netdev_odp_cb, &aux);
+        info->orig_in_port = aux.odp_port;
         old_stats = rte_flow_data->stats;
         modification = true;
         ret = netdev_offload_dpdk_flow_destroy(rte_flow_data);
@@ -1698,8 +1835,9 @@  netdev_offload_dpdk_flow_get(struct netdev *netdev,
         goto out;
     }
     attrs->dp_layer = "dpdk";
-    ret = netdev_dpdk_rte_flow_query_count(netdev, rte_flow_data->rte_flow,
-                                           &query, &error);
+    ret = netdev_dpdk_rte_flow_query_count(rte_flow_data->physdev,
+                                           rte_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),
@@ -1723,7 +1861,7 @@  netdev_offload_dpdk_flow_flush(struct netdev *netdev)
     struct ufid_to_rte_flow_data *data;
 
     CMAP_FOR_EACH (data, node, &ufid_to_rte_flow) {
-        if (data->netdev != netdev) {
+        if (data->netdev != netdev && data->physdev != netdev) {
             continue;
         }