diff mbox series

[ovs-dev,V4,04/17] netdev-offload-dpdk: Improve HW offload flow debuggability

Message ID 20191216151047.5967-5-elibr@mellanox.com
State Changes Requested
Delegated to: Ilya Maximets
Headers show
Series netdev datapath actions offload | expand

Commit Message

Eli Britstein Dec. 16, 2019, 3:10 p.m. UTC
Add debug prints when creating and destroying rte flows, with all the
flow details (attributes, patterns, actions).

Signed-off-by: Eli Britstein <elibr@mellanox.com>
Reviewed-by: Oz Shlomo <ozsh@mellanox.com>
---
 lib/netdev-offload-dpdk.c | 200 ++++++++++++++++++++++++++++++----------------
 1 file changed, 130 insertions(+), 70 deletions(-)

Comments

Ilya Maximets Dec. 16, 2019, 9:24 p.m. UTC | #1
On 16.12.2019 16:10, Eli Britstein wrote:
> Add debug prints when creating and destroying rte flows, with all the
> flow details (attributes, patterns, actions).
> 
> Signed-off-by: Eli Britstein <elibr@mellanox.com>
> Reviewed-by: Oz Shlomo <ozsh@mellanox.com>
> ---
>  lib/netdev-offload-dpdk.c | 200 ++++++++++++++++++++++++++++++----------------
>  1 file changed, 130 insertions(+), 70 deletions(-)
> 
> diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c
> index a5bc4ad33..7cb24ddcd 100644
> --- a/lib/netdev-offload-dpdk.c
> +++ b/lib/netdev-offload-dpdk.c
> @@ -28,6 +28,7 @@
>  #include "uuid.h"
>  
>  VLOG_DEFINE_THIS_MODULE(netdev_offload_dpdk);
> +static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(100, 5);

Why it's 'error_rl'?  You're using it for everything, not only for
errors.  It might be better to rename it to just 'rl'.  This will
allow to decrease line length in a few places and put commands on a
single line.

>  
>  /* Thread-safety
>   * =============
> @@ -132,72 +133,70 @@ struct flow_actions {
>  };
>  
>  static void
> -dump_flow_pattern(struct rte_flow_item *item)
> +dump_flow_attr(struct ds *s, const struct rte_flow_attr *attr)
>  {
> -    struct ds s;
> -
> -    if (!VLOG_IS_DBG_ENABLED() || item->type == RTE_FLOW_ITEM_TYPE_END) {
> -        return;
> -    }
> -
> -    ds_init(&s);
> +    ds_put_format(s,
> +                  "  Attributes: "
> +                  "ingress=%d, egress=%d, prio=%d, group=%d, transfer=%d\n",
> +                  attr->ingress, attr->egress, attr->priority, attr->group,
> +                  attr->transfer);
> +}
>  
> +static void
> +dump_flow_pattern(struct ds *s, const struct rte_flow_item *item)
> +{
>      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;
>  
> -        ds_put_cstr(&s, "rte flow eth pattern:\n");
> +        ds_put_cstr(s, "rte flow eth pattern:\n");
>          if (eth_spec) {
> -            ds_put_format(&s,
> +            ds_put_format(s,
>                            "  Spec: src="ETH_ADDR_FMT", dst="ETH_ADDR_FMT", "
>                            "type=0x%04" PRIx16"\n",
>                            ETH_ADDR_BYTES_ARGS(eth_spec->src.addr_bytes),
>                            ETH_ADDR_BYTES_ARGS(eth_spec->dst.addr_bytes),
>                            ntohs(eth_spec->type));
>          } else {
> -            ds_put_cstr(&s, "  Spec = null\n");
> +            ds_put_cstr(s, "  Spec = null\n");
>          }
>          if (eth_mask) {
> -            ds_put_format(&s,
> +            ds_put_format(s,
>                            "  Mask: src="ETH_ADDR_FMT", dst="ETH_ADDR_FMT", "
>                            "type=0x%04"PRIx16"\n",
>                            ETH_ADDR_BYTES_ARGS(eth_mask->src.addr_bytes),
>                            ETH_ADDR_BYTES_ARGS(eth_mask->dst.addr_bytes),
>                            ntohs(eth_mask->type));
>          } else {
> -            ds_put_cstr(&s, "  Mask = null\n");
> +            ds_put_cstr(s, "  Mask = null\n");
>          }
> -    }
> -
> -    if (item->type == RTE_FLOW_ITEM_TYPE_VLAN) {
> +    } else if (item->type == RTE_FLOW_ITEM_TYPE_VLAN) {
>          const struct rte_flow_item_vlan *vlan_spec = item->spec;
>          const struct rte_flow_item_vlan *vlan_mask = item->mask;
>  
> -        ds_put_cstr(&s, "rte flow vlan pattern:\n");
> +        ds_put_cstr(s, "rte flow vlan pattern:\n");
>          if (vlan_spec) {
> -            ds_put_format(&s,
> +            ds_put_format(s,
>                            "  Spec: inner_type=0x%"PRIx16", tci=0x%"PRIx16"\n",
>                            ntohs(vlan_spec->inner_type), ntohs(vlan_spec->tci));
>          } else {
> -            ds_put_cstr(&s, "  Spec = null\n");
> +            ds_put_cstr(s, "  Spec = null\n");
>          }
>  
>          if (vlan_mask) {
> -            ds_put_format(&s,
> +            ds_put_format(s,
>                            "  Mask: inner_type=0x%"PRIx16", tci=0x%"PRIx16"\n",
>                            ntohs(vlan_mask->inner_type), ntohs(vlan_mask->tci));
>          } else {
> -            ds_put_cstr(&s, "  Mask = null\n");
> +            ds_put_cstr(s, "  Mask = null\n");
>          }
> -    }
> -
> -    if (item->type == RTE_FLOW_ITEM_TYPE_IPV4) {
> +    } else if (item->type == RTE_FLOW_ITEM_TYPE_IPV4) {
>          const struct rte_flow_item_ipv4 *ipv4_spec = item->spec;
>          const struct rte_flow_item_ipv4 *ipv4_mask = item->mask;
>  
> -        ds_put_cstr(&s, "rte flow ipv4 pattern:\n");
> +        ds_put_cstr(s, "rte flow ipv4 pattern:\n");
>          if (ipv4_spec) {
> -            ds_put_format(&s,
> +            ds_put_format(s,
>                            "  Spec: tos=0x%"PRIx8", ttl=%"PRIx8
>                            ", proto=0x%"PRIx8
>                            ", src="IP_FMT", dst="IP_FMT"\n",
> @@ -207,10 +206,10 @@ dump_flow_pattern(struct rte_flow_item *item)
>                            IP_ARGS(ipv4_spec->hdr.src_addr),
>                            IP_ARGS(ipv4_spec->hdr.dst_addr));
>          } else {
> -            ds_put_cstr(&s, "  Spec = null\n");
> +            ds_put_cstr(s, "  Spec = null\n");
>          }
>          if (ipv4_mask) {
> -            ds_put_format(&s,
> +            ds_put_format(s,
>                            "  Mask: tos=0x%"PRIx8", ttl=%"PRIx8
>                            ", proto=0x%"PRIx8
>                            ", src="IP_FMT", dst="IP_FMT"\n",
> @@ -220,89 +219,81 @@ dump_flow_pattern(struct rte_flow_item *item)
>                            IP_ARGS(ipv4_mask->hdr.src_addr),
>                            IP_ARGS(ipv4_mask->hdr.dst_addr));
>          } else {
> -            ds_put_cstr(&s, "  Mask = null\n");
> +            ds_put_cstr(s, "  Mask = null\n");
>          }
> -    }
> -
> -    if (item->type == RTE_FLOW_ITEM_TYPE_UDP) {
> +    } else if (item->type == RTE_FLOW_ITEM_TYPE_UDP) {
>          const struct rte_flow_item_udp *udp_spec = item->spec;
>          const struct rte_flow_item_udp *udp_mask = item->mask;
>  
> -        ds_put_cstr(&s, "rte flow udp pattern:\n");
> +        ds_put_cstr(s, "rte flow udp pattern:\n");
>          if (udp_spec) {
> -            ds_put_format(&s,
> +            ds_put_format(s,
>                            "  Spec: src_port=%"PRIu16", dst_port=%"PRIu16"\n",
>                            ntohs(udp_spec->hdr.src_port),
>                            ntohs(udp_spec->hdr.dst_port));
>          } else {
> -            ds_put_cstr(&s, "  Spec = null\n");
> +            ds_put_cstr(s, "  Spec = null\n");
>          }
>          if (udp_mask) {
> -            ds_put_format(&s,
> +            ds_put_format(s,
>                            "  Mask: src_port=0x%"PRIx16
>                            ", dst_port=0x%"PRIx16"\n",
>                            ntohs(udp_mask->hdr.src_port),
>                            ntohs(udp_mask->hdr.dst_port));
>          } else {
> -            ds_put_cstr(&s, "  Mask = null\n");
> +            ds_put_cstr(s, "  Mask = null\n");
>          }
> -    }
> -
> -    if (item->type == RTE_FLOW_ITEM_TYPE_SCTP) {
> +    } else if (item->type == RTE_FLOW_ITEM_TYPE_SCTP) {
>          const struct rte_flow_item_sctp *sctp_spec = item->spec;
>          const struct rte_flow_item_sctp *sctp_mask = item->mask;
>  
> -        ds_put_cstr(&s, "rte flow sctp pattern:\n");
> +        ds_put_cstr(s, "rte flow sctp pattern:\n");
>          if (sctp_spec) {
> -            ds_put_format(&s,
> +            ds_put_format(s,
>                            "  Spec: src_port=%"PRIu16", dst_port=%"PRIu16"\n",
>                            ntohs(sctp_spec->hdr.src_port),
>                            ntohs(sctp_spec->hdr.dst_port));
>          } else {
> -            ds_put_cstr(&s, "  Spec = null\n");
> +            ds_put_cstr(s, "  Spec = null\n");
>          }
>          if (sctp_mask) {
> -            ds_put_format(&s,
> +            ds_put_format(s,
>                            "  Mask: src_port=0x%"PRIx16
>                            ", dst_port=0x%"PRIx16"\n",
>                            ntohs(sctp_mask->hdr.src_port),
>                            ntohs(sctp_mask->hdr.dst_port));
>          } else {
> -            ds_put_cstr(&s, "  Mask = null\n");
> +            ds_put_cstr(s, "  Mask = null\n");
>          }
> -    }
> -
> -    if (item->type == RTE_FLOW_ITEM_TYPE_ICMP) {
> +    } else if (item->type == RTE_FLOW_ITEM_TYPE_ICMP) {
>          const struct rte_flow_item_icmp *icmp_spec = item->spec;
>          const struct rte_flow_item_icmp *icmp_mask = item->mask;
>  
> -        ds_put_cstr(&s, "rte flow icmp pattern:\n");
> +        ds_put_cstr(s, "rte flow icmp pattern:\n");
>          if (icmp_spec) {
> -            ds_put_format(&s,
> +            ds_put_format(s,
>                            "  Spec: icmp_type=%"PRIu8", icmp_code=%"PRIu8"\n",
>                            icmp_spec->hdr.icmp_type,
>                            icmp_spec->hdr.icmp_code);
>          } else {
> -            ds_put_cstr(&s, "  Spec = null\n");
> +            ds_put_cstr(s, "  Spec = null\n");
>          }
>          if (icmp_mask) {
> -            ds_put_format(&s,
> +            ds_put_format(s,
>                            "  Mask: icmp_type=0x%"PRIx8
>                            ", icmp_code=0x%"PRIx8"\n",
>                            icmp_spec->hdr.icmp_type,
>                            icmp_spec->hdr.icmp_code);
>          } else {
> -            ds_put_cstr(&s, "  Mask = null\n");
> +            ds_put_cstr(s, "  Mask = null\n");
>          }
> -    }
> -
> -    if (item->type == RTE_FLOW_ITEM_TYPE_TCP) {
> +    } else if (item->type == RTE_FLOW_ITEM_TYPE_TCP) {
>          const struct rte_flow_item_tcp *tcp_spec = item->spec;
>          const struct rte_flow_item_tcp *tcp_mask = item->mask;
>  
> -        ds_put_cstr(&s, "rte flow tcp pattern:\n");
> +        ds_put_cstr(s, "rte flow tcp pattern:\n");
>          if (tcp_spec) {
> -            ds_put_format(&s,
> +            ds_put_format(s,
>                            "  Spec: src_port=%"PRIu16", dst_port=%"PRIu16
>                            ", data_off=0x%"PRIx8", tcp_flags=0x%"PRIx8"\n",
>                            ntohs(tcp_spec->hdr.src_port),
> @@ -310,10 +301,10 @@ dump_flow_pattern(struct rte_flow_item *item)
>                            tcp_spec->hdr.data_off,
>                            tcp_spec->hdr.tcp_flags);
>          } else {
> -            ds_put_cstr(&s, "  Spec = null\n");
> +            ds_put_cstr(s, "  Spec = null\n");
>          }
>          if (tcp_mask) {
> -            ds_put_format(&s,
> +            ds_put_format(s,
>                            "  Mask: src_port=%"PRIx16", dst_port=%"PRIx16
>                            ", data_off=0x%"PRIx8", tcp_flags=0x%"PRIx8"\n",
>                            ntohs(tcp_mask->hdr.src_port),
> @@ -321,12 +312,85 @@ dump_flow_pattern(struct rte_flow_item *item)
>                            tcp_mask->hdr.data_off,
>                            tcp_mask->hdr.tcp_flags);
>          } else {
> -            ds_put_cstr(&s, "  Mask = null\n");
> +            ds_put_cstr(s, "  Mask = null\n");
> +        }
> +    } else {
> +        ds_put_format(s, "unknown rte flow pattern (%d)\n", item->type);
> +    }
> +}
> +
> +static void
> +dump_flow_action(struct ds *s, const struct rte_flow_action *actions)
> +{
> +    if (actions->type == RTE_FLOW_ACTION_TYPE_MARK) {
> +        const struct rte_flow_action_mark *mark = actions->conf;
> +
> +        ds_put_cstr(s, "rte flow mark action:\n");
> +        if (mark) {
> +            ds_put_format(s, "  Mark: id=%d\n", mark->id);
> +        } else {
> +            ds_put_cstr(s, "  Mark = null\n");
> +        }
> +    } else if (actions->type == RTE_FLOW_ACTION_TYPE_RSS) {
> +        const struct rte_flow_action_rss *rss = actions->conf;
> +
> +        ds_put_cstr(s, "rte flow RSS action:\n");
> +        if (rss) {
> +            ds_put_format(s, "  RSS: queue_num=%d\n", rss->queue_num);
> +        } else {
> +            ds_put_cstr(s, "  RSS = null\n");
>          }
> +    } else {
> +        ds_put_format(s, "unknown rte flow action (%d)\n", actions->type);
> +    }
> +}
> +
> +static struct ds *
> +dump_flow(struct ds *s,
> +          const struct rte_flow_attr *attr,
> +          const struct rte_flow_item *items,
> +          const struct rte_flow_action *actions)
> +{
> +    if (attr) {
> +        dump_flow_attr(s, attr);
> +    }
> +    while (items && items->type != RTE_FLOW_ITEM_TYPE_END) {
> +        dump_flow_pattern(s, items++);
> +    }
> +    while (actions && actions->type != RTE_FLOW_ACTION_TYPE_END) {
> +        dump_flow_action(s, actions++);
>      }
> +    return s;
> +}
> +
> +static struct rte_flow *
> +netdev_offload_dpdk_flow_create(struct netdev *netdev,
> +                                const struct rte_flow_attr *attr,
> +                                const struct rte_flow_item *items,
> +                                const struct rte_flow_action *actions,
> +                                struct rte_flow_error *error)
> +{
> +    struct rte_flow *flow;
> +    struct ds s;
>  
> -    VLOG_DBG("%s", ds_cstr(&s));
> +    flow = netdev_dpdk_rte_flow_create(netdev, attr, items, actions, error);
> +    ds_init(&s);
> +    if (flow) {

I'd still prefer to not dump flows if debug is not enabled or will
drop the output, i.e. call it only if !VLOG_DROP_DBG(&rl).

> +        VLOG_DBG_RL(&error_rl, "Create rte_flow: netdev=%s\n"
> +                    "%s"
> +                    "Flow handle=%p\n",
> +                    netdev_get_name(netdev),
> +                    ds_cstr(dump_flow(&s, attr, items, actions)), flow);
> +    } else {
> +        VLOG_ERR_RL(&error_rl, "Create rte_flow: netdev=%s\n"
> +                    "%s"
> +                    "FAILED. error %u : message : %s\n",
> +                    netdev_get_name(netdev),
> +                    ds_cstr(dump_flow(&s, attr, items, actions)),
> +                    error->type, error->message);
> +    }
>      ds_destroy(&s);
> +    return flow;
>  }
>  
>  static void
> @@ -349,7 +413,6 @@ add_flow_pattern(struct flow_patterns *patterns, enum rte_flow_item_type type,
>      patterns->items[cnt].spec = spec;
>      patterns->items[cnt].mask = mask;
>      patterns->items[cnt].last = NULL;
> -    dump_flow_pattern(&patterns->items[cnt]);
>      patterns->cnt++;
>  }
>  
> @@ -650,13 +713,10 @@ netdev_offload_dpdk_add_flow(struct netdev *netdev,
>  
>      add_flow_mark_rss_actions(&actions, info->flow_mark, netdev);
>  
> -    flow = netdev_dpdk_rte_flow_create(netdev, &flow_attr,
> -                                       patterns.items,
> -                                       actions.actions, &error);
> +    flow = netdev_offload_dpdk_flow_create(netdev, &flow_attr, patterns.items,
> +                                           actions.actions, &error);
>  
>      if (!flow) {
> -        VLOG_ERR("%s: rte flow creat error: %u : message : %s\n",
> -                 netdev_get_name(netdev), error.type, error.message);
>          ret = -1;
>          goto out;
>      }
> @@ -756,8 +816,8 @@ netdev_offload_dpdk_destroy_flow(struct netdev *netdev,
>                   netdev_get_name(netdev), rte_flow,
>                   UUID_ARGS((struct uuid *)ufid));
>      } else {
> -        VLOG_ERR("%s: rte flow destroy error: %u : message : %s\n",
> -                 netdev_get_name(netdev), error.type, error.message);
> +        VLOG_ERR("%s: Failed to destroy flow: %s (%u)\n",
> +                 netdev_get_name(netdev), error.message, error.type);
>      }
>  
>      return ret;
>
diff mbox series

Patch

diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c
index a5bc4ad33..7cb24ddcd 100644
--- a/lib/netdev-offload-dpdk.c
+++ b/lib/netdev-offload-dpdk.c
@@ -28,6 +28,7 @@ 
 #include "uuid.h"
 
 VLOG_DEFINE_THIS_MODULE(netdev_offload_dpdk);
+static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(100, 5);
 
 /* Thread-safety
  * =============
@@ -132,72 +133,70 @@  struct flow_actions {
 };
 
 static void
-dump_flow_pattern(struct rte_flow_item *item)
+dump_flow_attr(struct ds *s, const struct rte_flow_attr *attr)
 {
-    struct ds s;
-
-    if (!VLOG_IS_DBG_ENABLED() || item->type == RTE_FLOW_ITEM_TYPE_END) {
-        return;
-    }
-
-    ds_init(&s);
+    ds_put_format(s,
+                  "  Attributes: "
+                  "ingress=%d, egress=%d, prio=%d, group=%d, transfer=%d\n",
+                  attr->ingress, attr->egress, attr->priority, attr->group,
+                  attr->transfer);
+}
 
+static void
+dump_flow_pattern(struct ds *s, const struct rte_flow_item *item)
+{
     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;
 
-        ds_put_cstr(&s, "rte flow eth pattern:\n");
+        ds_put_cstr(s, "rte flow eth pattern:\n");
         if (eth_spec) {
-            ds_put_format(&s,
+            ds_put_format(s,
                           "  Spec: src="ETH_ADDR_FMT", dst="ETH_ADDR_FMT", "
                           "type=0x%04" PRIx16"\n",
                           ETH_ADDR_BYTES_ARGS(eth_spec->src.addr_bytes),
                           ETH_ADDR_BYTES_ARGS(eth_spec->dst.addr_bytes),
                           ntohs(eth_spec->type));
         } else {
-            ds_put_cstr(&s, "  Spec = null\n");
+            ds_put_cstr(s, "  Spec = null\n");
         }
         if (eth_mask) {
-            ds_put_format(&s,
+            ds_put_format(s,
                           "  Mask: src="ETH_ADDR_FMT", dst="ETH_ADDR_FMT", "
                           "type=0x%04"PRIx16"\n",
                           ETH_ADDR_BYTES_ARGS(eth_mask->src.addr_bytes),
                           ETH_ADDR_BYTES_ARGS(eth_mask->dst.addr_bytes),
                           ntohs(eth_mask->type));
         } else {
-            ds_put_cstr(&s, "  Mask = null\n");
+            ds_put_cstr(s, "  Mask = null\n");
         }
-    }
-
-    if (item->type == RTE_FLOW_ITEM_TYPE_VLAN) {
+    } else if (item->type == RTE_FLOW_ITEM_TYPE_VLAN) {
         const struct rte_flow_item_vlan *vlan_spec = item->spec;
         const struct rte_flow_item_vlan *vlan_mask = item->mask;
 
-        ds_put_cstr(&s, "rte flow vlan pattern:\n");
+        ds_put_cstr(s, "rte flow vlan pattern:\n");
         if (vlan_spec) {
-            ds_put_format(&s,
+            ds_put_format(s,
                           "  Spec: inner_type=0x%"PRIx16", tci=0x%"PRIx16"\n",
                           ntohs(vlan_spec->inner_type), ntohs(vlan_spec->tci));
         } else {
-            ds_put_cstr(&s, "  Spec = null\n");
+            ds_put_cstr(s, "  Spec = null\n");
         }
 
         if (vlan_mask) {
-            ds_put_format(&s,
+            ds_put_format(s,
                           "  Mask: inner_type=0x%"PRIx16", tci=0x%"PRIx16"\n",
                           ntohs(vlan_mask->inner_type), ntohs(vlan_mask->tci));
         } else {
-            ds_put_cstr(&s, "  Mask = null\n");
+            ds_put_cstr(s, "  Mask = null\n");
         }
-    }
-
-    if (item->type == RTE_FLOW_ITEM_TYPE_IPV4) {
+    } else if (item->type == RTE_FLOW_ITEM_TYPE_IPV4) {
         const struct rte_flow_item_ipv4 *ipv4_spec = item->spec;
         const struct rte_flow_item_ipv4 *ipv4_mask = item->mask;
 
-        ds_put_cstr(&s, "rte flow ipv4 pattern:\n");
+        ds_put_cstr(s, "rte flow ipv4 pattern:\n");
         if (ipv4_spec) {
-            ds_put_format(&s,
+            ds_put_format(s,
                           "  Spec: tos=0x%"PRIx8", ttl=%"PRIx8
                           ", proto=0x%"PRIx8
                           ", src="IP_FMT", dst="IP_FMT"\n",
@@ -207,10 +206,10 @@  dump_flow_pattern(struct rte_flow_item *item)
                           IP_ARGS(ipv4_spec->hdr.src_addr),
                           IP_ARGS(ipv4_spec->hdr.dst_addr));
         } else {
-            ds_put_cstr(&s, "  Spec = null\n");
+            ds_put_cstr(s, "  Spec = null\n");
         }
         if (ipv4_mask) {
-            ds_put_format(&s,
+            ds_put_format(s,
                           "  Mask: tos=0x%"PRIx8", ttl=%"PRIx8
                           ", proto=0x%"PRIx8
                           ", src="IP_FMT", dst="IP_FMT"\n",
@@ -220,89 +219,81 @@  dump_flow_pattern(struct rte_flow_item *item)
                           IP_ARGS(ipv4_mask->hdr.src_addr),
                           IP_ARGS(ipv4_mask->hdr.dst_addr));
         } else {
-            ds_put_cstr(&s, "  Mask = null\n");
+            ds_put_cstr(s, "  Mask = null\n");
         }
-    }
-
-    if (item->type == RTE_FLOW_ITEM_TYPE_UDP) {
+    } else if (item->type == RTE_FLOW_ITEM_TYPE_UDP) {
         const struct rte_flow_item_udp *udp_spec = item->spec;
         const struct rte_flow_item_udp *udp_mask = item->mask;
 
-        ds_put_cstr(&s, "rte flow udp pattern:\n");
+        ds_put_cstr(s, "rte flow udp pattern:\n");
         if (udp_spec) {
-            ds_put_format(&s,
+            ds_put_format(s,
                           "  Spec: src_port=%"PRIu16", dst_port=%"PRIu16"\n",
                           ntohs(udp_spec->hdr.src_port),
                           ntohs(udp_spec->hdr.dst_port));
         } else {
-            ds_put_cstr(&s, "  Spec = null\n");
+            ds_put_cstr(s, "  Spec = null\n");
         }
         if (udp_mask) {
-            ds_put_format(&s,
+            ds_put_format(s,
                           "  Mask: src_port=0x%"PRIx16
                           ", dst_port=0x%"PRIx16"\n",
                           ntohs(udp_mask->hdr.src_port),
                           ntohs(udp_mask->hdr.dst_port));
         } else {
-            ds_put_cstr(&s, "  Mask = null\n");
+            ds_put_cstr(s, "  Mask = null\n");
         }
-    }
-
-    if (item->type == RTE_FLOW_ITEM_TYPE_SCTP) {
+    } else if (item->type == RTE_FLOW_ITEM_TYPE_SCTP) {
         const struct rte_flow_item_sctp *sctp_spec = item->spec;
         const struct rte_flow_item_sctp *sctp_mask = item->mask;
 
-        ds_put_cstr(&s, "rte flow sctp pattern:\n");
+        ds_put_cstr(s, "rte flow sctp pattern:\n");
         if (sctp_spec) {
-            ds_put_format(&s,
+            ds_put_format(s,
                           "  Spec: src_port=%"PRIu16", dst_port=%"PRIu16"\n",
                           ntohs(sctp_spec->hdr.src_port),
                           ntohs(sctp_spec->hdr.dst_port));
         } else {
-            ds_put_cstr(&s, "  Spec = null\n");
+            ds_put_cstr(s, "  Spec = null\n");
         }
         if (sctp_mask) {
-            ds_put_format(&s,
+            ds_put_format(s,
                           "  Mask: src_port=0x%"PRIx16
                           ", dst_port=0x%"PRIx16"\n",
                           ntohs(sctp_mask->hdr.src_port),
                           ntohs(sctp_mask->hdr.dst_port));
         } else {
-            ds_put_cstr(&s, "  Mask = null\n");
+            ds_put_cstr(s, "  Mask = null\n");
         }
-    }
-
-    if (item->type == RTE_FLOW_ITEM_TYPE_ICMP) {
+    } else if (item->type == RTE_FLOW_ITEM_TYPE_ICMP) {
         const struct rte_flow_item_icmp *icmp_spec = item->spec;
         const struct rte_flow_item_icmp *icmp_mask = item->mask;
 
-        ds_put_cstr(&s, "rte flow icmp pattern:\n");
+        ds_put_cstr(s, "rte flow icmp pattern:\n");
         if (icmp_spec) {
-            ds_put_format(&s,
+            ds_put_format(s,
                           "  Spec: icmp_type=%"PRIu8", icmp_code=%"PRIu8"\n",
                           icmp_spec->hdr.icmp_type,
                           icmp_spec->hdr.icmp_code);
         } else {
-            ds_put_cstr(&s, "  Spec = null\n");
+            ds_put_cstr(s, "  Spec = null\n");
         }
         if (icmp_mask) {
-            ds_put_format(&s,
+            ds_put_format(s,
                           "  Mask: icmp_type=0x%"PRIx8
                           ", icmp_code=0x%"PRIx8"\n",
                           icmp_spec->hdr.icmp_type,
                           icmp_spec->hdr.icmp_code);
         } else {
-            ds_put_cstr(&s, "  Mask = null\n");
+            ds_put_cstr(s, "  Mask = null\n");
         }
-    }
-
-    if (item->type == RTE_FLOW_ITEM_TYPE_TCP) {
+    } else if (item->type == RTE_FLOW_ITEM_TYPE_TCP) {
         const struct rte_flow_item_tcp *tcp_spec = item->spec;
         const struct rte_flow_item_tcp *tcp_mask = item->mask;
 
-        ds_put_cstr(&s, "rte flow tcp pattern:\n");
+        ds_put_cstr(s, "rte flow tcp pattern:\n");
         if (tcp_spec) {
-            ds_put_format(&s,
+            ds_put_format(s,
                           "  Spec: src_port=%"PRIu16", dst_port=%"PRIu16
                           ", data_off=0x%"PRIx8", tcp_flags=0x%"PRIx8"\n",
                           ntohs(tcp_spec->hdr.src_port),
@@ -310,10 +301,10 @@  dump_flow_pattern(struct rte_flow_item *item)
                           tcp_spec->hdr.data_off,
                           tcp_spec->hdr.tcp_flags);
         } else {
-            ds_put_cstr(&s, "  Spec = null\n");
+            ds_put_cstr(s, "  Spec = null\n");
         }
         if (tcp_mask) {
-            ds_put_format(&s,
+            ds_put_format(s,
                           "  Mask: src_port=%"PRIx16", dst_port=%"PRIx16
                           ", data_off=0x%"PRIx8", tcp_flags=0x%"PRIx8"\n",
                           ntohs(tcp_mask->hdr.src_port),
@@ -321,12 +312,85 @@  dump_flow_pattern(struct rte_flow_item *item)
                           tcp_mask->hdr.data_off,
                           tcp_mask->hdr.tcp_flags);
         } else {
-            ds_put_cstr(&s, "  Mask = null\n");
+            ds_put_cstr(s, "  Mask = null\n");
+        }
+    } else {
+        ds_put_format(s, "unknown rte flow pattern (%d)\n", item->type);
+    }
+}
+
+static void
+dump_flow_action(struct ds *s, const struct rte_flow_action *actions)
+{
+    if (actions->type == RTE_FLOW_ACTION_TYPE_MARK) {
+        const struct rte_flow_action_mark *mark = actions->conf;
+
+        ds_put_cstr(s, "rte flow mark action:\n");
+        if (mark) {
+            ds_put_format(s, "  Mark: id=%d\n", mark->id);
+        } else {
+            ds_put_cstr(s, "  Mark = null\n");
+        }
+    } else if (actions->type == RTE_FLOW_ACTION_TYPE_RSS) {
+        const struct rte_flow_action_rss *rss = actions->conf;
+
+        ds_put_cstr(s, "rte flow RSS action:\n");
+        if (rss) {
+            ds_put_format(s, "  RSS: queue_num=%d\n", rss->queue_num);
+        } else {
+            ds_put_cstr(s, "  RSS = null\n");
         }
+    } else {
+        ds_put_format(s, "unknown rte flow action (%d)\n", actions->type);
+    }
+}
+
+static struct ds *
+dump_flow(struct ds *s,
+          const struct rte_flow_attr *attr,
+          const struct rte_flow_item *items,
+          const struct rte_flow_action *actions)
+{
+    if (attr) {
+        dump_flow_attr(s, attr);
+    }
+    while (items && items->type != RTE_FLOW_ITEM_TYPE_END) {
+        dump_flow_pattern(s, items++);
+    }
+    while (actions && actions->type != RTE_FLOW_ACTION_TYPE_END) {
+        dump_flow_action(s, actions++);
     }
+    return s;
+}
+
+static struct rte_flow *
+netdev_offload_dpdk_flow_create(struct netdev *netdev,
+                                const struct rte_flow_attr *attr,
+                                const struct rte_flow_item *items,
+                                const struct rte_flow_action *actions,
+                                struct rte_flow_error *error)
+{
+    struct rte_flow *flow;
+    struct ds s;
 
-    VLOG_DBG("%s", ds_cstr(&s));
+    flow = netdev_dpdk_rte_flow_create(netdev, attr, items, actions, error);
+    ds_init(&s);
+    if (flow) {
+        VLOG_DBG_RL(&error_rl, "Create rte_flow: netdev=%s\n"
+                    "%s"
+                    "Flow handle=%p\n",
+                    netdev_get_name(netdev),
+                    ds_cstr(dump_flow(&s, attr, items, actions)), flow);
+    } else {
+        VLOG_ERR_RL(&error_rl, "Create rte_flow: netdev=%s\n"
+                    "%s"
+                    "FAILED. error %u : message : %s\n",
+                    netdev_get_name(netdev),
+                    ds_cstr(dump_flow(&s, attr, items, actions)),
+                    error->type, error->message);
+    }
     ds_destroy(&s);
+    return flow;
 }
 
 static void
@@ -349,7 +413,6 @@  add_flow_pattern(struct flow_patterns *patterns, enum rte_flow_item_type type,
     patterns->items[cnt].spec = spec;
     patterns->items[cnt].mask = mask;
     patterns->items[cnt].last = NULL;
-    dump_flow_pattern(&patterns->items[cnt]);
     patterns->cnt++;
 }
 
@@ -650,13 +713,10 @@  netdev_offload_dpdk_add_flow(struct netdev *netdev,
 
     add_flow_mark_rss_actions(&actions, info->flow_mark, netdev);
 
-    flow = netdev_dpdk_rte_flow_create(netdev, &flow_attr,
-                                       patterns.items,
-                                       actions.actions, &error);
+    flow = netdev_offload_dpdk_flow_create(netdev, &flow_attr, patterns.items,
+                                           actions.actions, &error);
 
     if (!flow) {
-        VLOG_ERR("%s: rte flow creat error: %u : message : %s\n",
-                 netdev_get_name(netdev), error.type, error.message);
         ret = -1;
         goto out;
     }
@@ -756,8 +816,8 @@  netdev_offload_dpdk_destroy_flow(struct netdev *netdev,
                  netdev_get_name(netdev), rte_flow,
                  UUID_ARGS((struct uuid *)ufid));
     } else {
-        VLOG_ERR("%s: rte flow destroy error: %u : message : %s\n",
-                 netdev_get_name(netdev), error.type, error.message);
+        VLOG_ERR("%s: Failed to destroy flow: %s (%u)\n",
+                 netdev_get_name(netdev), error.message, error.type);
     }
 
     return ret;