Skip to contentSkip to conversation

ethdev: add API to dump device internal flow info

PATCH Superseded Xiaoyu Min 7 participants 24 messages Jan 16, 2020 – Jan 20, 2020

Subsystems (2)
lore
Share & tools
lore

Xiaoyu Min

author
#0 lore raw

cover ethdev: add API to dump device internal flow info

This serial patchs is based on RFC [1] and the comments on it.

A new rte flow API is added to dump device internal representation information for the offloaded rte flows.

This is very helpful for user and developer to debug flow offloading stuff, i.e, to check whether PMD offloads the rte flow in a correct way from HW perspective.

The output file is in vendor specific format. An external vendor provided tool may be needed in order to get human readable info.

[1]: https://patches.dpdk.org/cover/64597/

Xiaoyu Min (2):
ethdev: add API to dump device internal flow info net/mlx5: support flow dump API

Xueming Li (3):
app/testpmd: new flow dump CLI
net/mlx5: add socket server for external tools
doc: update mlx5 document for flow dump feature

 app/test-pmd/cmdline_flow.c              |  91 +++++++++
 app/test-pmd/config.c                    |  27 +++
 app/test-pmd/testpmd.h                   |   1 +
 doc/guides/nics/mlx5.rst                 |  28 +++
 drivers/net/mlx5/Makefile                |   8 +-
 drivers/net/mlx5/meson.build             |   3 +
 drivers/net/mlx5/mlx5.c                  |   2 +
 drivers/net/mlx5/mlx5.h                  |   9 +
 drivers/net/mlx5/mlx5_devx_cmds.c        |  35 ++++
 drivers/net/mlx5/mlx5_flow.c             |  24 +++
 drivers/net/mlx5/mlx5_glue.c             |  13 ++
 drivers/net/mlx5/mlx5_glue.h             |   1 +
 drivers/net/mlx5/mlx5_socket.c           | 227 +++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |   3 +
 lib/librte_ethdev/rte_flow.c             |  16 ++
 lib/librte_ethdev/rte_flow.h             |  21 +++
 lib/librte_ethdev/rte_flow_driver.h      |   5 +
 17 files changed, 513 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/mlx5/mlx5_socket.c
Signature · 1 line
2.24.1
author
#1 lore raw

PATCH 1/5 ethdev: add API to dump device internal flow info

Introduce an API which dump the device's internal representation information of rte flows in hardware.

---
 lib/librte_ethdev/rte_ethdev_version.map |  3 +++
 lib/librte_ethdev/rte_flow.c             | 16 ++++++++++++++++
 lib/librte_ethdev/rte_flow.h             | 21 +++++++++++++++++++++
 lib/librte_ethdev/rte_flow_driver.h      |  5 +++++
 4 files changed, 45 insertions(+)
lib/librte_ethdev/rte_ethdev_version.map +3 -0
index a7dacf2cf2..3f32fdecf7 100644
@@ -227,4 +227,7 @@ EXPERIMENTAL {
 	rte_flow_dynf_metadata_mask;
 	rte_flow_dynf_metadata_register;
 	rte_eth_dev_set_ptypes;
+
+	# added in 20.02
+	rte_flow_dev_dump;
 };
lib/librte_ethdev/rte_flow.c +16 -0
index 87a3e8c4c6..751ce721b2 100644
@@ -1212,3 +1212,19 @@ rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
 	}
 	return lsize;
 }
+
+int
+rte_flow_dev_dump(uint16_t port_id, FILE *file, struct rte_flow_error *error)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
+
+	if (unlikely(!ops))
+		return -rte_errno;
+	if (likely(!!ops->dev_dump))
+		return flow_err(port_id, ops->dev_dump(dev, file, error),
+				error);
+	return rte_flow_error_set(error, ENOSYS,
+				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				  NULL, rte_strerror(ENOSYS));
+}
lib/librte_ethdev/rte_flow.h +21 -0
index 58b50265d2..cf7cf61ae8 100644
@@ -2790,6 +2790,27 @@ enum rte_flow_conv_op {
 	RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
 };
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump hardware internal representation information of
+ * rte flow to file.
+ *
+ * @param[in] port_id
+ *    The port identifier of the Ethernet device.
+ * @param[in] file
+ *   A pointer to a file for output.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL. PMDs initialize this
+ *   structure in case of error only.
+ * @return
+ *   0 on success, a nagative value otherwise.
+ */
+__rte_experimental
+int
+rte_flow_dev_dump(uint16_t port_id, FILE *file, struct rte_flow_error *error);
+
 /**
  * Check if mbuf dynamic field for metadata is registered.
  *
lib/librte_ethdev/rte_flow_driver.h +5 -0
index a0359853e6..51a9a57a0f 100644
@@ -96,6 +96,11 @@ struct rte_flow_ops {
 		(struct rte_eth_dev *,
 		 int,
 		 struct rte_flow_error *);
+	/** See rte_flow_dev_dump(). */
+	int (*dev_dump)
+		(struct rte_eth_dev *dev,
+		 FILE *file,
+		 struct rte_flow_error *error);
 };
 
 /**
Signature · 1 line
2.24.1
#2 lore raw
Quoted 5 lines · Xiaoyu Min

Introduce an API which dump the device's internal representation information of rte flows in hardware.

Looks good to me.

Quoted 94 lines
---
 lib/librte_ethdev/rte_ethdev_version.map |  3 +++
 lib/librte_ethdev/rte_flow.c             | 16 ++++++++++++++++
 lib/librte_ethdev/rte_flow.h             | 21 +++++++++++++++++++++
 lib/librte_ethdev/rte_flow_driver.h      |  5 +++++
 4 files changed, 45 insertions(+)
lib/librte_ethdev/rte_ethdev_version.map +3 -0
index a7dacf2cf2..3f32fdecf7 100644
@@ -227,4 +227,7 @@ EXPERIMENTAL {
        rte_flow_dynf_metadata_mask;
        rte_flow_dynf_metadata_register;
        rte_eth_dev_set_ptypes;
+
+       # added in 20.02
+       rte_flow_dev_dump;
 };
lib/librte_ethdev/rte_flow.c +16 -0
index 87a3e8c4c6..751ce721b2 100644
@@ -1212,3 +1212,19 @@ rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
        }
        return lsize;
 }
+
+int
+rte_flow_dev_dump(uint16_t port_id, FILE *file, struct rte_flow_error *error)
+{
+       struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+       const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
+
+       if (unlikely(!ops))
+               return -rte_errno;
+       if (likely(!!ops->dev_dump))
+               return flow_err(port_id, ops->dev_dump(dev, file, error),
+                               error);
+       return rte_flow_error_set(error, ENOSYS,
+                                 RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+                                 NULL, rte_strerror(ENOSYS));
+}
lib/librte_ethdev/rte_flow.h +21 -0
index 58b50265d2..cf7cf61ae8 100644
@@ -2790,6 +2790,27 @@ enum rte_flow_conv_op {
        RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
 };
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump hardware internal representation information of
+ * rte flow to file.
+ *
+ * @param[in] port_id
+ *    The port identifier of the Ethernet device.
+ * @param[in] file
+ *   A pointer to a file for output.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL. PMDs initialize this
+ *   structure in case of error only.
+ * @return
+ *   0 on success, a nagative value otherwise.
+ */
+__rte_experimental
+int
+rte_flow_dev_dump(uint16_t port_id, FILE *file, struct rte_flow_error *error);
+
 /**
  * Check if mbuf dynamic field for metadata is registered.
  *
lib/librte_ethdev/rte_flow_driver.h +5 -1
index a0359853e6..51a9a57a0f 100644
@@ -96,6 +96,11 @@ struct rte_flow_ops {
                (struct rte_eth_dev *,
                 int,
                 struct rte_flow_error *);
+       /** See rte_flow_dev_dump(). */
+       int (*dev_dump)
+               (struct rte_eth_dev *dev,
+                FILE *file,
+                struct rte_flow_error *error);
 };
 
 /**
--

2.24.1

Acked-by
#3 lore raw
Quoted 6 lines

-----Original Message-----
<[email protected]>; dpdk-dev <[email protected]>
Subject: Re: [PATCH 1/5] ethdev: add API to dump device internal flow info

Quoted 8 lines
Quoted 5 lines · Xiaoyu Min

Introduce an API which dump the device's internal representation information of rte flows in hardware.

Looks good to me.

Thanks,
Ori

Quoted 100 lines

Review

Quoted 9 lines
---
 lib/librte_ethdev/rte_ethdev_version.map |  3 +++
 lib/librte_ethdev/rte_flow.c             | 16 ++++++++++++++++
 lib/librte_ethdev/rte_flow.h             | 21 +++++++++++++++++++++
 lib/librte_ethdev/rte_flow_driver.h      |  5 +++++
 4 files changed, 45 insertions(+)
(quoted diff) +0 -0

b/lib/librte_ethdev/rte_ethdev_version.map

lib/librte_ethdev/rte_flow.c rte_flow_expand_rss(struct

Quoted 16 lines

index a7dacf2cf2..3f32fdecf7 100644

lib/librte_ethdev/rte_ethdev_version.map +3 -0
@@ -227,4 +227,7 @@ EXPERIMENTAL {
        rte_flow_dynf_metadata_mask;
        rte_flow_dynf_metadata_register;
        rte_eth_dev_set_ptypes;
+
+       # added in 20.02
+       rte_flow_dev_dump;
 };
lib/librte_ethdev/rte_flow.c +0 -0
index 87a3e8c4c6..751ce721b2 100644
@@ -1212,3 +1212,19 @@ rte_flow_expand_rss(struct

rte_flow_expand_rss *buf, size_t size,

lib/librte_ethdev/rte_flow.c rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,

Quoted 6 lines
lib/librte_ethdev/rte_flow.c +3 -0
        }
        return lsize;
 }
+
+int
+rte_flow_dev_dump(uint16_t port_id, FILE *file, struct rte_flow_error

*error)

Quoted 31 lines
lib/librte_ethdev/rte_flow.c +13 -0
+{
+       struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+       const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
+
+       if (unlikely(!ops))
+               return -rte_errno;
+       if (likely(!!ops->dev_dump))
+               return flow_err(port_id, ops->dev_dump(dev, file, error),
+                               error);
+       return rte_flow_error_set(error, ENOSYS,
+                                 RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+                                 NULL, rte_strerror(ENOSYS));
+}
lib/librte_ethdev/rte_flow.h +10 -0
index 58b50265d2..cf7cf61ae8 100644
@@ -2790,6 +2790,27 @@ enum rte_flow_conv_op {
        RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
 };
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump hardware internal representation information of
+ * rte flow to file.
+ *
+ * @param[in] port_id
+ *    The port identifier of the Ethernet device.
+ * @param[in] file

lib/librte_ethdev/rte_flow.h enum rte_flow_conv_op { · line 2812

Quoted 10 lines
lib/librte_ethdev/rte_flow.h +10 -0
+ *   A pointer to a file for output.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL. PMDs initialize this
+ *   structure in case of error only.
+ * @return
+ *   0 on success, a nagative value otherwise.
+ */
+__rte_experimental
+int
+rte_flow_dev_dump(uint16_t port_id, FILE *file, struct rte_flow_error

*error);

Review

Quoted 5 lines
lib/librte_ethdev/rte_flow.h +1 -0
+
 /**
  * Check if mbuf dynamic field for metadata is registered.
  *
(quoted diff) +0 -0

b/lib/librte_ethdev/rte_flow_driver.h

Quoted 18 lines

index a0359853e6..51a9a57a0f 100644

lib/librte_ethdev/rte_flow_driver.h +5 -1
@@ -96,6 +96,11 @@ struct rte_flow_ops {
                (struct rte_eth_dev *,
                 int,
                 struct rte_flow_error *);
+       /** See rte_flow_dev_dump(). */
+       int (*dev_dump)
+               (struct rte_eth_dev *dev,
+                FILE *file,
+                struct rte_flow_error *error);
 };
 
 /**
--

2.24.1

#4 lore raw
In reply to Xiaoyu Min
Quoted 4 lines · Xiaoyu Min

Introduce an API which dump the device's internal representation information of rte flows in hardware.

<...>

Quoted 10 lines
lib/librte_ethdev/rte_flow.c +6 -0
@@ -1212,3 +1212,19 @@ rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
 	}
 	return lsize;
 }
+
+int
+rte_flow_dev_dump(uint16_t port_id, FILE *file, struct rte_flow_error *error)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);

lib/librte_ethdev/rte_flow.c rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size, · line 1230

Quoted 10 lines
lib/librte_ethdev/rte_flow.c +10 -0
+
+	if (unlikely(!ops))
+		return -rte_errno;
+	if (likely(!!ops->dev_dump))
+		return flow_err(port_id, ops->dev_dump(dev, file, error),
+				error);
+	return rte_flow_error_set(error, ENOSYS,
+				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				  NULL, rte_strerror(ENOSYS));
+}

Should API validate user provided input "FILE *file" ?

#5 lore raw
Quoted 22 lines · On Thu, 16 Jan 2020 20:37:36 +0000 Ferruh Yigit <[email protected]> wrote:
Quoted 4 lines · Xiaoyu Min

Introduce an API which dump the device's internal representation information of rte flows in hardware.

<...>

Quoted 14 lines
lib/librte_ethdev/rte_flow.c +10 -0
@@ -1212,3 +1212,19 @@ rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
 	}
 	return lsize;
 }
+
+int
+rte_flow_dev_dump(uint16_t port_id, FILE *file, struct rte_flow_error *error)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
+
+	if (unlikely(!ops))
+		return -rte_errno;
+	if (likely(!!ops->dev_dump))
Quoted 8 lines

lib/librte_ethdev/rte_flow.c rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size, · line 1230

Quoted 6 lines
lib/librte_ethdev/rte_flow.c +6 -0
+		return flow_err(port_id, ops->dev_dump(dev, file, error),
+				error);
+	return rte_flow_error_set(error, ENOSYS,
+				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				  NULL, rte_strerror(ENOSYS));
+}  

Should API validate user provided input "FILE *file" ?

None of the other DPDK dump routines do.

#6 lore raw
Quoted 34 lines · Stephen Hemminger
Quoted 22 lines · On Thu, 16 Jan 2020 20:37:36 +0000 Ferruh Yigit <[email protected]> wrote:
Quoted 4 lines · Xiaoyu Min

Introduce an API which dump the device's internal representation information of rte flows in hardware.

<...>

Quoted 14 lines
lib/librte_ethdev/rte_flow.c +10 -0
@@ -1212,3 +1212,19 @@ rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
 	}
 	return lsize;
 }
+
+int
+rte_flow_dev_dump(uint16_t port_id, FILE *file, struct rte_flow_error *error)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
+
+	if (unlikely(!ops))
+		return -rte_errno;
+	if (likely(!!ops->dev_dump))
Quoted 8 lines

lib/librte_ethdev/rte_flow.c rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size, · line 1230

Quoted 6 lines
lib/librte_ethdev/rte_flow.c +6 -0
+		return flow_err(port_id, ops->dev_dump(dev, file, error),
+				error);
+	return rte_flow_error_set(error, ENOSYS,
+				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				  NULL, rte_strerror(ENOSYS));
+}  

Should API validate user provided input "FILE *file" ?

Quoted 2 lines

None of the other DPDK dump routines do.

You are right, a few samples I checked doesn't validating it, I wonder if this is intentional decision or just missed the user input validation. For me, APIs should validate user input.

author
#7 lore raw

PATCH 2/5 net/mlx5: support flow dump API

In reply to Xiaoyu Min

Dump fdb/nic_rx/nic_tx raw flow data into specified file.

---
 drivers/net/mlx5/Makefile         |  7 ++++++-
 drivers/net/mlx5/meson.build      |  2 ++
 drivers/net/mlx5/mlx5.h           |  4 ++++
 drivers/net/mlx5/mlx5_devx_cmds.c | 35 +++++++++++++++++++++++++++++++
 drivers/net/mlx5/mlx5_flow.c      | 24 +++++++++++++++++++++
 drivers/net/mlx5/mlx5_glue.c      | 13 ++++++++++++
 drivers/net/mlx5/mlx5_glue.h      |  1 +
 7 files changed, 85 insertions(+), 1 deletion(-)
drivers/net/mlx5/Makefile +6 -1
index c5cf4397ac..6e5921b292 100644
@@ -8,7 +8,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
 LIB = librte_pmd_mlx5.a
 LIB_GLUE = $(LIB_GLUE_BASE).$(LIB_GLUE_VERSION)
 LIB_GLUE_BASE = librte_pmd_mlx5_glue.so
-LIB_GLUE_VERSION = 19.08.0
+LIB_GLUE_VERSION = 19.11.0
 
 # Sources.
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5.c
@@ -203,6 +203,11 @@ mlx5_autoconf.h.new: $(RTE_SDK)/buildtools/auto-config-h.sh
 		infiniband/mlx5dv.h \
 		func mlx5dv_dr_action_create_flow_meter \
 		$(AUTOCONF_OUTPUT)
+	$Q sh -- '$<' '$@' \
+		HAVE_MLX5_DR_FLOW_DUMP \
+		infiniband/mlx5dv.h \
+		func mlx5dv_dump_dr_domain \
+		$(AUTOCONF_OUTPUT)
 	$Q sh -- '$<' '$@' \
 		HAVE_MLX5DV_MMAP_GET_NC_PAGES_CMD \
 		infiniband/mlx5dv.h \
drivers/net/mlx5/meson.build +2 -0
index d6b32db794..d7ef032afb 100644
@@ -186,6 +186,8 @@ if build
 		'RDMA_NLDEV_ATTR_PORT_INDEX' ],
 		[ 'HAVE_RDMA_NLDEV_ATTR_NDEV_INDEX', 'rdma/rdma_netlink.h',
 		'RDMA_NLDEV_ATTR_NDEV_INDEX' ],
+		[ 'HAVE_MLX5_DR_FLOW_DUMP', 'infiniband/mlx5dv.h',
+		'mlx5dv_dump_dr_domain'],
 	]
 	config = configuration_data()
 	foreach arg:has_sym_args
drivers/net/mlx5/mlx5.h +4 -0
index c3df8256ce..047181b32e 100644
@@ -975,6 +975,8 @@ struct mlx5_flow_counter *mlx5_counter_alloc(struct rte_eth_dev *dev);
 void mlx5_counter_free(struct rte_eth_dev *dev, struct mlx5_flow_counter *cnt);
 int mlx5_counter_query(struct rte_eth_dev *dev, struct mlx5_flow_counter *cnt,
 		       bool clear, uint64_t *pkts, uint64_t *bytes);
+int mlx5_flow_dev_dump(struct rte_eth_dev *dev, FILE *file,
+		       struct rte_flow_error *error);
 
 /* mlx5_mp.c */
 void mlx5_mp_req_start_rxtx(struct rte_eth_dev *dev);
@@ -1049,6 +1051,8 @@ struct mlx5_devx_obj *mlx5_devx_cmd_create_tis
 	(struct ibv_context *ctx, struct mlx5_devx_tis_attr *tis_attr);
 struct mlx5_devx_obj *mlx5_devx_cmd_create_td(struct ibv_context *ctx);
 
+int mlx5_devx_cmd_flow_dump(struct mlx5_ibv_shared *sh, FILE *file);
+
 /* mlx5_flow_meter.c */
 
 int mlx5_flow_meter_ops_get(struct rte_eth_dev *dev, void *arg);
drivers/net/mlx5/mlx5_devx_cmds.c +35 -0
index 9893287ba8..d6bf15689d 100644
@@ -927,3 +927,38 @@ mlx5_devx_cmd_create_td(struct ibv_context *ctx)
 			   transport_domain);
 	return td;
 }
+
+/**
+ * Dump all flows to file.
+ *
+ * @param[in] sh
+ *   Pointer to context.
+ * @param[out] file
+ *   Pointer to file stream.
+ *
+ * @return
+ *   0 on success, a nagative value otherwise.
+ */
+int
+mlx5_devx_cmd_flow_dump(struct mlx5_ibv_shared *sh __rte_unused,
+			FILE *file __rte_unused)
+{
+	int ret = 0;
+
+#ifdef HAVE_MLX5_DR_FLOW_DUMP
+	if (sh->fdb_domain) {
+		ret = mlx5_glue->dr_dump_domain(file, sh->fdb_domain);
+		if (ret)
+			return ret;
+	}
+	assert(sh->rx_domain);
+	ret = mlx5_glue->dr_dump_domain(file, sh->rx_domain);
+	if (ret)
+		return ret;
+	assert(sh->tx_domain);
+	ret = mlx5_glue->dr_dump_domain(file, sh->tx_domain);
+#else
+	ret = ENOTSUP;
+#endif
+	return -ret;
+}
drivers/net/mlx5/mlx5_flow.c +24 -0
index cb9d265f6f..0126cd8f92 100644
@@ -236,6 +236,7 @@ static const struct rte_flow_ops mlx5_flow_ops = {
 	.flush = mlx5_flow_flush,
 	.isolate = mlx5_flow_isolate,
 	.query = mlx5_flow_query,
+	.dev_dump = mlx5_flow_dev_dump,
 };
 
 /* Convert FDIR request to Generic flow. */
@@ -5679,3 +5680,26 @@ mlx5_flow_discover_mreg_c(struct rte_eth_dev *dev)
 		config->flow_mreg_c[n] = REG_NONE;
 	return 0;
 }
+
+/**
+ * Dump flow raw hw data to file
+ *
+ * @param[in] dev
+ *    The pointer to Ethernet device.
+ * @param[in] file
+ *   A pointer to a file for output.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL. PMDs initialize this
+ *   structure in case of error only.
+ * @return
+ *   0 on success, a nagative value otherwise.
+ */
+int
+mlx5_flow_dev_dump(struct rte_eth_dev *dev,
+		   FILE *file,
+		   struct rte_flow_error *error __rte_unused)
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+
+	return mlx5_devx_cmd_flow_dump(priv->sh, file);
+}
drivers/net/mlx5/mlx5_glue.c +13 -0
index 0917bf28d6..4906eebc01 100644
@@ -1037,6 +1037,18 @@ mlx5_glue_devx_port_query(struct ibv_context *ctx,
 #endif
 }
 
+static int
+mlx5_glue_dr_dump_domain(FILE *file, void *domain)
+{
+#ifdef HAVE_MLX5_DR_FLOW_DUMP
+	return mlx5dv_dump_dr_domain(file, domain);
+#else
+	RTE_SET_USED(file);
+	RTE_SET_USED(domain);
+	return -ENOTSUP;
+#endif
+}
+
 alignas(RTE_CACHE_LINE_SIZE)
 const struct mlx5_glue *mlx5_glue = &(const struct mlx5_glue){
 	.version = MLX5_GLUE_VERSION,
@@ -1134,4 +1146,5 @@ const struct mlx5_glue *mlx5_glue = &(const struct mlx5_glue){
 	.devx_umem_dereg = mlx5_glue_devx_umem_dereg,
 	.devx_qp_query = mlx5_glue_devx_qp_query,
 	.devx_port_query = mlx5_glue_devx_port_query,
+	.dr_dump_domain = mlx5_glue_dr_dump_domain,
 };
drivers/net/mlx5/mlx5_glue.h +1 -0
index 6442f1eba8..6771a18c64 100644
@@ -256,6 +256,7 @@ struct mlx5_glue {
 	int (*devx_port_query)(struct ibv_context *ctx,
 			       uint32_t port_num,
 			       struct mlx5dv_devx_port *mlx5_devx_port);
+	int (*dr_dump_domain)(FILE *file, void *domain);
 };
 
 const struct mlx5_glue *mlx5_glue;
Signature · 1 line
2.24.1
author
#8 lore raw

PATCH 3/5 app/testpmd: new flow dump CLI

In reply to Xiaoyu Min

From: Xueming Li <[email protected]>

New flow dump CLI to dump device internal representation information of flows into screen.

---
 app/test-pmd/cmdline_flow.c | 91 +++++++++++++++++++++++++++++++++++++
 app/test-pmd/config.c       | 27 +++++++++++
 app/test-pmd/testpmd.h      |  1 +
 3 files changed, 119 insertions(+)
app/test-pmd/cmdline_flow.c +91 -0
index 99dade7d8c..19336e5d42 100644
@@ -41,6 +41,7 @@ enum index {
 	BOOLEAN,
 	STRING,
 	HEX,
+	FILE_PATH,
 	MAC_ADDR,
 	IPV4_ADDR,
 	IPV6_ADDR,
@@ -63,6 +64,7 @@ enum index {
 	CREATE,
 	DESTROY,
 	FLUSH,
+	DUMP,
 	QUERY,
 	LIST,
 	ISOLATE,
@@ -631,6 +633,9 @@ struct buffer {
 			uint32_t *rule;
 			uint32_t rule_n;
 		} destroy; /**< Destroy arguments. */
+		struct {
+			char file[128];
+		} dump; /**< Dump arguments. */
 		struct {
 			uint32_t rule;
 			struct rte_flow_action action;
@@ -685,6 +690,12 @@ static const enum index next_destroy_attr[] = {
 	ZERO,
 };
 
+static const enum index next_dump_attr[] = {
+	FILE_PATH,
+	END,
+	ZERO,
+};
+
 static const enum index next_list_attr[] = {
 	LIST_GROUP,
 	END,
@@ -1374,6 +1385,9 @@ static int parse_destroy(struct context *, const struct token *,
 static int parse_flush(struct context *, const struct token *,
 		       const char *, unsigned int,
 		       void *, unsigned int);
+static int parse_dump(struct context *, const struct token *,
+		      const char *, unsigned int,
+		      void *, unsigned int);
 static int parse_query(struct context *, const struct token *,
 		       const char *, unsigned int,
 		       void *, unsigned int);
@@ -1401,6 +1415,9 @@ static int parse_string(struct context *, const struct token *,
 static int parse_hex(struct context *ctx, const struct token *token,
 			const char *str, unsigned int len,
 			void *buf, unsigned int size);
+static int parse_string0(struct context *, const struct token *,
+			const char *, unsigned int,
+			void *, unsigned int);
 static int parse_mac_addr(struct context *, const struct token *,
 			  const char *, unsigned int,
 			  void *, unsigned int);
@@ -1494,6 +1511,12 @@ static const struct token token_list[] = {
 		.type = "HEX",
 		.help = "fixed string",
 		.call = parse_hex,
+	},
+	[FILE_PATH] = {
+		.name = "{file path}",
+		.type = "STRING",
+		.help = "file path",
+		.call = parse_string0,
 		.comp = comp_none,
 	},
 	[MAC_ADDR] = {
@@ -1555,6 +1578,7 @@ static const struct token token_list[] = {
 			      CREATE,
 			      DESTROY,
 			      FLUSH,
+			      DUMP,
 			      LIST,
 			      QUERY,
 			      ISOLATE)),
@@ -1589,6 +1613,14 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
 		.call = parse_flush,
 	},
+	[DUMP] = {
+		.name = "dump",
+		.help = "dump all flow rules to file",
+		.next = NEXT(next_dump_attr, NEXT_ENTRY(PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.dump.file),
+			     ARGS_ENTRY(struct buffer, port)),
+		.call = parse_dump,
+	},
 	[QUERY] = {
 		.name = "query",
 		.help = "query an existing flow rule",
@@ -5012,6 +5044,33 @@ parse_flush(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse tokens for dump command. */
+static int
+parse_dump(struct context *ctx, const struct token *token,
+	    const char *str, unsigned int len,
+	    void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+
+	/* Token name must match. */
+	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+		return -1;
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return len;
+	if (!out->command) {
+		if (ctx->curr != DUMP)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+	}
+	return len;
+}
+
 /** Parse tokens for query command. */
 static int
 parse_query(struct context *ctx, const struct token *token,
@@ -5409,6 +5468,35 @@ parse_hex(struct context *ctx, const struct token *token,
 
 }
 
+/**
+ * Parse a zero-ended string.
+ */
+static int
+parse_string0(struct context *ctx, const struct token *token __rte_unused,
+	     const char *str, unsigned int len,
+	     void *buf, unsigned int size)
+{
+	const struct arg *arg_data = pop_args(ctx);
+
+	/* Arguments are expected. */
+	if (!arg_data)
+		return -1;
+	size = arg_data->size;
+	/* Bit-mask fill is not supported. */
+	if (arg_data->mask || size < len + 1)
+		goto error;
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg_data->offset;
+	strncpy(buf, str, len);
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg_data->offset, 0xff, len);
+	return len;
+error:
+	push_args(ctx, arg_data);
+	return -1;
+}
+
 /**
  * Parse a MAC address.
  *
@@ -6068,6 +6156,9 @@ cmd_flow_parsed(const struct buffer *in)
 	case FLUSH:
 		port_flow_flush(in->port);
 		break;
+	case DUMP:
+		port_flow_dump(in->port, in->args.dump.file);
+		break;
 	case QUERY:
 		port_flow_query(in->port, in->args.query.rule,
 				&in->args.query.action);
app/test-pmd/config.c +27 -0
index 9da1ffb034..1b4bdf7bf3 100644
@@ -1441,6 +1441,33 @@ port_flow_flush(portid_t port_id)
 	return ret;
 }
 
+/** Dump all flow rules. */
+int
+port_flow_dump(portid_t port_id, const char *file_name)
+{
+	int ret = 0;
+	FILE *file = stdout;
+	struct rte_flow_error error;
+
+	if (file_name && strlen(file_name)) {
+		file = fopen(file_name, "w");
+		if (!file) {
+			printf("Failed to create file %s: %s\n", file_name,
+			       strerror(errno));
+			return -errno;
+		}
+	}
+	ret = rte_flow_dev_dump(port_id, file, &error);
+	if (ret) {
+		port_flow_complain(&error);
+		printf("Failed to dump flow: %s\n", strerror(-ret));
+	} else
+		printf("Flow dump finished\n");
+	if (file_name && strlen(file_name))
+		fclose(file);
+	return ret;
+}
+
 /** Query a flow rule. */
 int
 port_flow_query(portid_t port_id, uint32_t rule,
app/test-pmd/testpmd.h +1 -0
index 857a11f8de..e1b9aba360 100644
@@ -734,6 +734,7 @@ int port_flow_create(portid_t port_id,
 		     const struct rte_flow_action *actions);
 int port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule);
 int port_flow_flush(portid_t port_id);
+int port_flow_dump(portid_t port_id, const char *file_name);
 int port_flow_query(portid_t port_id, uint32_t rule,
 		    const struct rte_flow_action *action);
 void port_flow_list(portid_t port_id, uint32_t n, const uint32_t *group);
Signature · 1 line
2.24.1
Acked-by on v1 3/5
#9 lore raw

Re: [dpdk-dev] [PATCH 3/5] app/testpmd: new flow dump CLI

Quoted 5 lines

-----Original Message-----
Subject: [PATCH 3/5] app/testpmd: new flow dump CLI

Quoted 8 lines

From: Xueming Li <[email protected]>

New flow dump CLI to dump device internal representation information of flows into screen.

---

Thanks,
Ori

Quoted 242 lines
 app/test-pmd/cmdline_flow.c | 91
+++++++++++++++++++++++++++++++++++++
 app/test-pmd/config.c       | 27 +++++++++++
 app/test-pmd/testpmd.h      |  1 +
 3 files changed, 119 insertions(+)
app/test-pmd/cmdline_flow.c +14 -0
index 99dade7d8c..19336e5d42 100644
@@ -41,6 +41,7 @@ enum index {
 	BOOLEAN,
 	STRING,
 	HEX,
+	FILE_PATH,
 	MAC_ADDR,
 	IPV4_ADDR,
 	IPV6_ADDR,
@@ -63,6 +64,7 @@ enum index {
 	CREATE,
 	DESTROY,
 	FLUSH,
+	DUMP,
 	QUERY,
 	LIST,
 	ISOLATE,
@@ -631,6 +633,9 @@ struct buffer {
 			uint32_t *rule;
 			uint32_t rule_n;
 		} destroy; /**< Destroy arguments. */
+		struct {
+			char file[128];
+		} dump; /**< Dump arguments. */
 		struct {
 			uint32_t rule;
 			struct rte_flow_action action;
@@ -685,6 +690,12 @@ static const enum index next_destroy_attr[] = {
 	ZERO,
 };
 
+static const enum index next_dump_attr[] = {
+	FILE_PATH,
+	END,
+	ZERO,
+};
+
 static const enum index next_list_attr[] = {
 	LIST_GROUP,
 	END,
@@ -1374,6 +1385,9 @@ static int parse_destroy(struct context *, const
 struct token *,
 static int parse_flush(struct context *, const struct token *,
 		       const char *, unsigned int,
 		       void *, unsigned int);
+static int parse_dump(struct context *, const struct token *,
+		      const char *, unsigned int,
+		      void *, unsigned int);
 static int parse_query(struct context *, const struct token *,
 		       const char *, unsigned int,
app/test-pmd/cmdline_flow.c +3 -0
 		       void *, unsigned int);
@@ -1401,6 +1415,9 @@ static int parse_string(struct context *, const struct
 token *,
 static int parse_hex(struct context *ctx, const struct token *token,
 			const char *str, unsigned int len,
 			void *buf, unsigned int size);
+static int parse_string0(struct context *, const struct token *,
+			const char *, unsigned int,
+			void *, unsigned int);
 static int parse_mac_addr(struct context *, const struct token *,
 			  const char *, unsigned int,
app/test-pmd/cmdline_flow.c +42 -0
 			  void *, unsigned int);
@@ -1494,6 +1511,12 @@ static const struct token token_list[] = {
 		.type = "HEX",
 		.help = "fixed string",
 		.call = parse_hex,
+	},
+	[FILE_PATH] = {
+		.name = "{file path}",
+		.type = "STRING",
+		.help = "file path",
+		.call = parse_string0,
 		.comp = comp_none,
 	},
 	[MAC_ADDR] = {
@@ -1555,6 +1578,7 @@ static const struct token token_list[] = {
 			      CREATE,
 			      DESTROY,
 			      FLUSH,
+			      DUMP,
 			      LIST,
 			      QUERY,
 			      ISOLATE)),
@@ -1589,6 +1613,14 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
 		.call = parse_flush,
 	},
+	[DUMP] = {
+		.name = "dump",
+		.help = "dump all flow rules to file",
+		.next = NEXT(next_dump_attr, NEXT_ENTRY(PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.dump.file),
+			     ARGS_ENTRY(struct buffer, port)),
+		.call = parse_dump,
+	},
 	[QUERY] = {
 		.name = "query",
 		.help = "query an existing flow rule",
@@ -5012,6 +5044,33 @@ parse_flush(struct context *ctx, const struct token
 *token,
 	return len;
 }
 
+/** Parse tokens for dump command. */
+static int
+parse_dump(struct context *ctx, const struct token *token,
+	    const char *str, unsigned int len,
+	    void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+
+	/* Token name must match. */
+	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+		return -1;
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return len;
+	if (!out->command) {
+		if (ctx->curr != DUMP)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+	}
+	return len;
+}
+
 /** Parse tokens for query command. */
 static int

parse_query(struct context *ctx, const struct token *token,

app/test-pmd/cmdline_flow.c +29 -0
@@ -5409,6 +5468,35 @@ parse_hex(struct context *ctx, const struct token
 *token,
 
 }
 
+/**
+ * Parse a zero-ended string.
+ */
+static int
+parse_string0(struct context *ctx, const struct token *token __rte_unused,
+	     const char *str, unsigned int len,
+	     void *buf, unsigned int size)
+{
+	const struct arg *arg_data = pop_args(ctx);
+
+	/* Arguments are expected. */
+	if (!arg_data)
+		return -1;
+	size = arg_data->size;
+	/* Bit-mask fill is not supported. */
+	if (arg_data->mask || size < len + 1)
+		goto error;
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg_data->offset;
+	strncpy(buf, str, len);
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg_data->offset, 0xff,
 len);
+	return len;
+error:
+	push_args(ctx, arg_data);
+	return -1;
+}
+
 /**

* Parse a MAC address.
*

app/test-pmd/cmdline_flow.c +3 -0
@@ -6068,6 +6156,9 @@ cmd_flow_parsed(const struct buffer *in)
 	case FLUSH:
 		port_flow_flush(in->port);
 		break;
+	case DUMP:
+		port_flow_dump(in->port, in->args.dump.file);
+		break;
 	case QUERY:
 		port_flow_query(in->port, in->args.query.rule,
 				&in->args.query.action);
app/test-pmd/config.c +27 -0
index 9da1ffb034..1b4bdf7bf3 100644
@@ -1441,6 +1441,33 @@ port_flow_flush(portid_t port_id)
 	return ret;
 }
 
+/** Dump all flow rules. */
+int
+port_flow_dump(portid_t port_id, const char *file_name)
+{
+	int ret = 0;
+	FILE *file = stdout;
+	struct rte_flow_error error;
+
+	if (file_name && strlen(file_name)) {
+		file = fopen(file_name, "w");
+		if (!file) {
+			printf("Failed to create file %s: %s\n", file_name,
+			       strerror(errno));
+			return -errno;
+		}
+	}
+	ret = rte_flow_dev_dump(port_id, file, &error);
+	if (ret) {
+		port_flow_complain(&error);
+		printf("Failed to dump flow: %s\n", strerror(-ret));
+	} else
+		printf("Flow dump finished\n");
+	if (file_name && strlen(file_name))
+		fclose(file);
+	return ret;
+}
+
 /** Query a flow rule. */
 int
 port_flow_query(portid_t port_id, uint32_t rule,
app/test-pmd/testpmd.h +1 -1
index 857a11f8de..e1b9aba360 100644
@@ -734,6 +734,7 @@ int port_flow_create(portid_t port_id,
 		     const struct rte_flow_action *actions);
 int port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule);
 int port_flow_flush(portid_t port_id);
+int port_flow_dump(portid_t port_id, const char *file_name);
 int port_flow_query(portid_t port_id, uint32_t rule,
 		    const struct rte_flow_action *action);
 void port_flow_list(portid_t port_id, uint32_t n, const uint32_t *group);
--

2.24.1

author
#10 lore raw

PATCH 4/5 net/mlx5: add socket server for external tools

In reply to Xiaoyu Min

From: Xueming Li <[email protected]>

Add pmd unix socket server to enable external tool applications to trigger flow dump.

Socket path:

	/var/tmp/dpdk_mlx5_<pid>

Socket format:
io_raw: port_id of uint16
file: file descriptor of int

---
 drivers/net/mlx5/Makefile      |   1 +
 drivers/net/mlx5/meson.build   |   1 +
 drivers/net/mlx5/mlx5.c        |   2 +
 drivers/net/mlx5/mlx5.h        |   5 +
 drivers/net/mlx5/mlx5_socket.c | 227 +++++++++++++++++++++++++++++++++
 5 files changed, 236 insertions(+)
 create mode 100644 drivers/net/mlx5/mlx5_socket.c
drivers/net/mlx5/Makefile +1 -0
index 6e5921b292..397e29dcbb 100644
@@ -39,6 +39,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_mp.c
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_nl.c
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_devx_cmds.c
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_utils.c
+SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_socket.c
 
 ifeq ($(CONFIG_RTE_IBVERBS_LINK_DLOPEN),y)
 INSTALL-$(CONFIG_RTE_LIBRTE_MLX5_PMD)-lib += $(LIB_GLUE)
drivers/net/mlx5/meson.build +1 -0
index d7ef032afb..cb2fb90495 100644
@@ -61,6 +61,7 @@ if build
 		'mlx5_vlan.c',
 		'mlx5_devx_cmds.c',
 		'mlx5_utils.c',
+		'mlx5_socket.c',
 	)
 	if (dpdk_conf.has('RTE_ARCH_X86_64')
 		or dpdk_conf.has('RTE_ARCH_ARM64')
drivers/net/mlx5/mlx5.c +2 -0
index 50960c91ce..ffee39c1a0 100644
@@ -2954,6 +2954,8 @@ mlx5_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
 	struct mlx5_dev_config dev_config;
 	int ret;
 
+	if (rte_eal_process_type() == RTE_PROC_PRIMARY)
+		mlx5_pmd_socket_init();
 	ret = mlx5_init_once();
 	if (ret) {
 		DRV_LOG(ERR, "unable to init PMD global data: %s",
drivers/net/mlx5/mlx5.h +5 -0
index 047181b32e..ceb6de821e 100644
@@ -990,6 +990,11 @@ void mlx5_mp_uninit_primary(void);
 int mlx5_mp_init_secondary(void);
 void mlx5_mp_uninit_secondary(void);
 
+/* mlx5_socket.c */
+
+int mlx5_pmd_socket_init(void);
+void mlx5_pmd_socket_uninit(void);
+
 /* mlx5_nl.c */
 
 int mlx5_nl_init(int protocol);
drivers/net/mlx5/mlx5_socket.c new file +227 -0

227 lines not on this page · raw message

Signature · 1 line
2.24.1
#11 lore raw

Re: [dpdk-dev] [PATCH 4/5] net/mlx5: add socket server for external tools

Hi, Jack

Quoted 167 lines

-----Original Message-----
From: Xiaoyu Min <[email protected]>
Sent: Thursday, January 16, 2020 12:14
To: [email protected]; Ori Kam <[email protected]>; Slava Ovsiienko <[email protected]>; Matan Azrad <[email protected]>; Raslan Darawsheh <[email protected]>; Shahaf Shuler <[email protected]>; Anatoly Burakov <[email protected]>
Cc: [email protected]; Xueming(Steven) Li <[email protected]>
Subject: [PATCH 4/5] net/mlx5: add socket server for external tools

From: Xueming Li <[email protected]>

Add pmd unix socket server to enable external tool applications to trigger flow dump.

Socket path:

	/var/tmp/dpdk_mlx5_<pid>

Socket format:
io_raw: port_id of uint16
file: file descriptor of int

---
 drivers/net/mlx5/Makefile      |   1 +
 drivers/net/mlx5/meson.build   |   1 +
 drivers/net/mlx5/mlx5.c        |   2 +
 drivers/net/mlx5/mlx5.h        |   5 +
 drivers/net/mlx5/mlx5_socket.c | 227 +++++++++++++++++++++++++++++++++
 5 files changed, 236 insertions(+)
 create mode 100644 drivers/net/mlx5/mlx5_socket.c
drivers/net/mlx5/Makefile index renamed from drivers/net/mlx5/Makefile +0 -0

6e5921b292..397e29dcbb 100644

drivers/net/mlx5/Makefile +1 -0
@@ -39,6 +39,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_mp.c
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_nl.c
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_devx_cmds.c
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_utils.c
+SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_socket.c
 
 ifeq ($(CONFIG_RTE_IBVERBS_LINK_DLOPEN),y)
 INSTALL-$(CONFIG_RTE_LIBRTE_MLX5_PMD)-lib += $(LIB_GLUE) diff --git

a/drivers/net/mlx5/meson.build b/drivers/net/mlx5/meson.build index d7ef032afb..cb2fb90495 100644

drivers/net/mlx5/meson.build +1 -0
@@ -61,6 +61,7 @@ if build
 		'mlx5_vlan.c',
 		'mlx5_devx_cmds.c',
 		'mlx5_utils.c',
+		'mlx5_socket.c',
 	)
 	if (dpdk_conf.has('RTE_ARCH_X86_64')
 		or dpdk_conf.has('RTE_ARCH_ARM64')
drivers/net/mlx5/mlx5.c index renamed from drivers/net/mlx5/mlx5.c +0 -0

50960c91ce..ffee39c1a0 100644

drivers/net/mlx5/mlx5.c +2 -0
@@ -2954,6 +2954,8 @@ mlx5_pci_probe(struct rte_pci_driver *pci_drv
 __rte_unused,
 	struct mlx5_dev_config dev_config;
 	int ret;
 
+	if (rte_eal_process_type() == RTE_PROC_PRIMARY)
+		mlx5_pmd_socket_init();
 	ret = mlx5_init_once();
 	if (ret) {

DRV_LOG(ERR, "unable to init PMD global data: %s", diff --git
a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h index 047181b32e..ceb6de821e 100644

drivers/net/mlx5/mlx5.h +5 -0
@@ -990,6 +990,11 @@ void mlx5_mp_uninit_primary(void);  int
 mlx5_mp_init_secondary(void);  void mlx5_mp_uninit_secondary(void);
 
+/* mlx5_socket.c */
+
+int mlx5_pmd_socket_init(void);
+void mlx5_pmd_socket_uninit(void);
+
 /* mlx5_nl.c */
 
 int mlx5_nl_init(int protocol);
drivers/net/mlx5/mlx5_socket.c new file +27 -0
new file mode 100644 index 0000000000..e4c93c4312
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019 Mellanox Technologies, Ltd  */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "rte_eal.h"
+#include "mlx5_utils.h"
+#include "mlx5.h"
+
+/* PMD socket service for tools. */
+
+int server_socket; /* Unix socket for primary process. */ struct
+rte_intr_handle server_intr_handle; /* Interrupt handler. */
+
+static void
+mlx5_pmd_make_path(struct sockaddr_un *addr, int pid) {
+	snprintf(addr->sun_path, sizeof(addr->sun_path),

"/var/tmp/dpdk_%s_%d",

drivers/net/mlx5/mlx5_socket.c new file +49 -0
+		 MLX5_DRIVER_NAME, pid);
+}
+
+/**
+ * Handle server pmd socket interrupts.
+ */
+static void
+mlx5_pmd_socket_handle(void *cb __rte_unused) {
+	int conn_sock;
+	int ret = -1;
+	struct cmsghdr *cmsg = NULL;
+	int data;
+	char buf[CMSG_SPACE(sizeof(int))] = { 0 };
+	struct iovec io = {
+		.iov_base = &data,
+		.iov_len = sizeof(data),
+	};
+	struct msghdr msg = {
+		.msg_iov = &io,
+		.msg_iovlen = 1,
+		.msg_control = buf,
+		.msg_controllen = sizeof(buf),
+	};
+	uint16_t port_id;
+	int fd;
+	FILE *file = NULL;
+	struct rte_eth_dev *dev;
+
+	/* Accept the connection from the client. */
+	conn_sock = accept(server_socket, NULL, NULL);
+	if (conn_sock < 0) {
+		DRV_LOG(WARNING, "connection failed: %s", strerror(errno));
+		return;
+	}
+	ret = recvmsg(conn_sock, &msg, MSG_WAITALL);
+	if (ret < 0) {
+		DRV_LOG(WARNING, "wrong message received: %s",
+			strerror(errno));
+		goto error;
+	}
+	/* Receive file descriptor. */
+	cmsg = CMSG_FIRSTHDR(&msg);
+	if (cmsg == NULL || cmsg->cmsg_type != SCM_RIGHTS ||
+	    cmsg->cmsg_len < sizeof(int)) {
+		DRV_LOG(WARNING, "invalid file descriptor message");
+		goto error;
+	}
+	memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd));
+	file = fdopen(fd, "w");

drivers/net/mlx5/mlx5_socket.c line 89

Quoted 10 lines
drivers/net/mlx5/mlx5_socket.c new file +10 -0
+	if (!file) {
+		DRV_LOG(WARNING, "Failed to open file");
+		goto error;
+	}
+	/* Receive port number. */
+	if (msg.msg_iovlen != 1 || msg.msg_iov->iov_len < sizeof(uint16_t)) {
+		DRV_LOG(WARNING, "wrong port number message");
+		goto error;
+	}
+	memcpy(&port_id, msg.msg_iov->iov_base, sizeof(port_id));

I would add the port_id check here.
The message with wrong port_id may crash the server.

Quoted 134 lines
drivers/net/mlx5/mlx5_socket.c new file +110 -0
+	/* Dump flow. */
+	dev = &rte_eth_devices[port_id];
+	ret = mlx5_flow_dev_dump(dev, file, NULL);
+	/* Set-up the ancillary data and reply. */
+	msg.msg_controllen = 0;
+	msg.msg_control = NULL;
+	msg.msg_iovlen = 1;
+	msg.msg_iov = &io;
+	data = -ret;
+	io.iov_len = sizeof(data);
+	io.iov_base = &data;
+	do {
+		ret = sendmsg(conn_sock, &msg, 0);
+	} while (ret < 0 && errno == EINTR);
+	if (ret < 0)
+		DRV_LOG(WARNING, "failed to send response %s",
+			strerror(errno));
+error:
+	if (conn_sock > 0)
+		close(conn_sock);
+	if (file)
+		fclose(file);
+}
+
+/**
+ * Install interrupt handler.
+ *
+ * @param dev
+ *   Pointer to Ethernet device.
+ * @return
+ *   0 on success, a negative errno value otherwise.
+ */
+static int
+mlx5_pmd_interrupt_handler_install(void)
+{
+	assert(server_socket);
+	server_intr_handle.fd = server_socket;
+	server_intr_handle.type = RTE_INTR_HANDLE_EXT;
+	return rte_intr_callback_register(&server_intr_handle,
+					  mlx5_pmd_socket_handle, NULL);
+}
+
+/**
+ * Uninstall interrupt handler.
+ */
+static void
+mlx5_pmd_interrupt_handler_uninstall(void)
+{
+	if (server_socket) {
+		mlx5_intr_callback_unregister(&server_intr_handle,
+					      mlx5_pmd_socket_handle,
+					      NULL);
+	}
+	server_intr_handle.fd = 0;
+	server_intr_handle.type = RTE_INTR_HANDLE_UNKNOWN; }
+
+/**
+ * Initialise the socket to communicate with the secondary process
+ *
+ * @param[in] dev
+ *   Pointer to Ethernet device.
+ *
+ * @return
+ *   0 on success, a negative value otherwise.
+ */
+int
+mlx5_pmd_socket_init(void)
+{
+	struct sockaddr_un sun = {
+		.sun_family = AF_UNIX,
+	};
+	int ret = -1;
+	int flags;
+
+	assert(rte_eal_process_type() == RTE_PROC_PRIMARY);
+	if (server_socket)
+		return 0;
+	/*
+	 * Initialize the socket to communicate with the secondary
+	 * process.
+	 */
+	ret = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (ret < 0) {
+		DRV_LOG(WARNING, "Failed to open mlx5 socket: %s",
+			strerror(errno));
+		goto error;
+	}
+	server_socket = ret;
+	flags = fcntl(server_socket, F_GETFL, 0);
+	if (flags == -1)
+		goto error;
+	ret = fcntl(server_socket, F_SETFL, flags | O_NONBLOCK);
+	if (ret < 0)
+		goto error;
+	mlx5_pmd_make_path(&sun, getpid());
+	remove(sun.sun_path);
+	ret = bind(server_socket, (const struct sockaddr *)&sun, sizeof(sun));
+	if (ret < 0) {
+		DRV_LOG(WARNING,
+			"cannot bind mlx5 socket: %s", strerror(errno));
+		goto close;
+	}
+	ret = listen(server_socket, 0);
+	if (ret < 0) {
+		DRV_LOG(WARNING, "cannot listen on mlx5 socket: %s",
+			strerror(errno));
+		goto close;
+	}
+	if (mlx5_pmd_interrupt_handler_install()) {
+		DRV_LOG(WARNING, "cannot register interrupt handler for

mlx5 socket: %s",

drivers/net/mlx5/mlx5_socket.c new file +23 -0
+			strerror(errno));
+		goto close;
+	}
+	return 0;
+close:
+	remove(sun.sun_path);
+error:
+	claim_zero(close(server_socket));
+	server_socket = 0;
+	DRV_LOG(ERR, "Cannot initialize socket: %s", strerror(errno));
+	return -errno;
+}
+
+/**
+ * Un-Initialize the pmd socket
+ */
+void __attribute__((destructor))
+mlx5_pmd_socket_uninit(void)
+{
+	if (!server_socket)
+		return;
+	mlx5_pmd_interrupt_handler_uninstall();
+	MKSTR(path, "/var/tmp/dpdk_%s_%d", MLX5_DRIVER_NAME,
Quoted 1 line

getpid());

I would move the MKSTR a couple lines below, after the close() call.

Quoted 6 lines
drivers/net/mlx5/mlx5_socket.c new file +4 -1
+	claim_zero(close(server_socket));
+	server_socket = 0;
+	claim_zero(remove(path));
+}
--

2.24.1

With best regards, Slava

author
#12 lore raw

Re: [dpdk-dev] [PATCH 4/5] net/mlx5: add socket server for external tools

Quoted 173 lines · Slava Ovsiienko

Hi, Jack

Quoted 171 lines

-----Original Message-----
From: Xiaoyu Min <[email protected]>
Sent: Thursday, January 16, 2020 12:14
To: [email protected]; Ori Kam <[email protected]>; Slava Ovsiienko <[email protected]>; Matan Azrad <[email protected]>; Raslan Darawsheh <[email protected]>; Shahaf Shuler <[email protected]>; Anatoly Burakov <[email protected]>
Cc: [email protected]; Xueming(Steven) Li <[email protected]>
Subject: [PATCH 4/5] net/mlx5: add socket server for external tools

From: Xueming Li <[email protected]>

Add pmd unix socket server to enable external tool applications to trigger flow dump.

Socket path:

	/var/tmp/dpdk_mlx5_<pid>

Socket format:
io_raw: port_id of uint16
file: file descriptor of int

---
 drivers/net/mlx5/Makefile      |   1 +
 drivers/net/mlx5/meson.build   |   1 +
 drivers/net/mlx5/mlx5.c        |   2 +
 drivers/net/mlx5/mlx5.h        |   5 +
 drivers/net/mlx5/mlx5_socket.c | 227 +++++++++++++++++++++++++++++++++
 5 files changed, 236 insertions(+)
 create mode 100644 drivers/net/mlx5/mlx5_socket.c
drivers/net/mlx5/Makefile index renamed from drivers/net/mlx5/Makefile +0 -0

6e5921b292..397e29dcbb 100644

drivers/net/mlx5/Makefile +1 -0
@@ -39,6 +39,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_mp.c
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_nl.c
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_devx_cmds.c
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_utils.c
+SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_socket.c
 
 ifeq ($(CONFIG_RTE_IBVERBS_LINK_DLOPEN),y)
 INSTALL-$(CONFIG_RTE_LIBRTE_MLX5_PMD)-lib += $(LIB_GLUE) diff --git

a/drivers/net/mlx5/meson.build b/drivers/net/mlx5/meson.build index d7ef032afb..cb2fb90495 100644

drivers/net/mlx5/meson.build +1 -0
@@ -61,6 +61,7 @@ if build
 		'mlx5_vlan.c',
 		'mlx5_devx_cmds.c',
 		'mlx5_utils.c',
+		'mlx5_socket.c',
 	)
 	if (dpdk_conf.has('RTE_ARCH_X86_64')
 		or dpdk_conf.has('RTE_ARCH_ARM64')
drivers/net/mlx5/mlx5.c index renamed from drivers/net/mlx5/mlx5.c +0 -0

50960c91ce..ffee39c1a0 100644

drivers/net/mlx5/mlx5.c +2 -0
@@ -2954,6 +2954,8 @@ mlx5_pci_probe(struct rte_pci_driver *pci_drv
 __rte_unused,
 	struct mlx5_dev_config dev_config;
 	int ret;
 
+	if (rte_eal_process_type() == RTE_PROC_PRIMARY)
+		mlx5_pmd_socket_init();
 	ret = mlx5_init_once();
 	if (ret) {

DRV_LOG(ERR, "unable to init PMD global data: %s", diff --git
a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h index 047181b32e..ceb6de821e 100644

drivers/net/mlx5/mlx5.h +5 -0
@@ -990,6 +990,11 @@ void mlx5_mp_uninit_primary(void);  int
 mlx5_mp_init_secondary(void);  void mlx5_mp_uninit_secondary(void);
 
+/* mlx5_socket.c */
+
+int mlx5_pmd_socket_init(void);
+void mlx5_pmd_socket_uninit(void);
+
 /* mlx5_nl.c */
 
 int mlx5_nl_init(int protocol);
drivers/net/mlx5/mlx5_socket.c new file +27 -0
new file mode 100644 index 0000000000..e4c93c4312
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019 Mellanox Technologies, Ltd  */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "rte_eal.h"
+#include "mlx5_utils.h"
+#include "mlx5.h"
+
+/* PMD socket service for tools. */
+
+int server_socket; /* Unix socket for primary process. */ struct
+rte_intr_handle server_intr_handle; /* Interrupt handler. */
+
+static void
+mlx5_pmd_make_path(struct sockaddr_un *addr, int pid) {
+	snprintf(addr->sun_path, sizeof(addr->sun_path),

"/var/tmp/dpdk_%s_%d",

drivers/net/mlx5/mlx5_socket.c new file +53 -0
+		 MLX5_DRIVER_NAME, pid);
+}
+
+/**
+ * Handle server pmd socket interrupts.
+ */
+static void
+mlx5_pmd_socket_handle(void *cb __rte_unused) {
+	int conn_sock;
+	int ret = -1;
+	struct cmsghdr *cmsg = NULL;
+	int data;
+	char buf[CMSG_SPACE(sizeof(int))] = { 0 };
+	struct iovec io = {
+		.iov_base = &data,
+		.iov_len = sizeof(data),
+	};
+	struct msghdr msg = {
+		.msg_iov = &io,
+		.msg_iovlen = 1,
+		.msg_control = buf,
+		.msg_controllen = sizeof(buf),
+	};
+	uint16_t port_id;
+	int fd;
+	FILE *file = NULL;
+	struct rte_eth_dev *dev;
+
+	/* Accept the connection from the client. */
+	conn_sock = accept(server_socket, NULL, NULL);
+	if (conn_sock < 0) {
+		DRV_LOG(WARNING, "connection failed: %s", strerror(errno));
+		return;
+	}
+	ret = recvmsg(conn_sock, &msg, MSG_WAITALL);
+	if (ret < 0) {
+		DRV_LOG(WARNING, "wrong message received: %s",
+			strerror(errno));
+		goto error;
+	}
+	/* Receive file descriptor. */
+	cmsg = CMSG_FIRSTHDR(&msg);
+	if (cmsg == NULL || cmsg->cmsg_type != SCM_RIGHTS ||
+	    cmsg->cmsg_len < sizeof(int)) {
+		DRV_LOG(WARNING, "invalid file descriptor message");
+		goto error;
+	}
+	memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd));
+	file = fdopen(fd, "w");
+	if (!file) {
+		DRV_LOG(WARNING, "Failed to open file");
+		goto error;
+	}
Quoted 8 lines

drivers/net/mlx5/mlx5_socket.c line 89

Quoted 6 lines
drivers/net/mlx5/mlx5_socket.c new file +6 -0
+	/* Receive port number. */
+	if (msg.msg_iovlen != 1 || msg.msg_iov->iov_len < sizeof(uint16_t)) {
+		DRV_LOG(WARNING, "wrong port number message");
+		goto error;
+	}
+	memcpy(&port_id, msg.msg_iov->iov_base, sizeof(port_id));

I would add the port_id check here.
The message with wrong port_id may crash the server.

Yes, you are right. I'll update it in v2.

Quoted 130 lines
Quoted 130 lines
drivers/net/mlx5/mlx5_socket.c new file +110 -0
+	/* Dump flow. */
+	dev = &rte_eth_devices[port_id];
+	ret = mlx5_flow_dev_dump(dev, file, NULL);
+	/* Set-up the ancillary data and reply. */
+	msg.msg_controllen = 0;
+	msg.msg_control = NULL;
+	msg.msg_iovlen = 1;
+	msg.msg_iov = &io;
+	data = -ret;
+	io.iov_len = sizeof(data);
+	io.iov_base = &data;
+	do {
+		ret = sendmsg(conn_sock, &msg, 0);
+	} while (ret < 0 && errno == EINTR);
+	if (ret < 0)
+		DRV_LOG(WARNING, "failed to send response %s",
+			strerror(errno));
+error:
+	if (conn_sock > 0)
+		close(conn_sock);
+	if (file)
+		fclose(file);
+}
+
+/**
+ * Install interrupt handler.
+ *
+ * @param dev
+ *   Pointer to Ethernet device.
+ * @return
+ *   0 on success, a negative errno value otherwise.
+ */
+static int
+mlx5_pmd_interrupt_handler_install(void)
+{
+	assert(server_socket);
+	server_intr_handle.fd = server_socket;
+	server_intr_handle.type = RTE_INTR_HANDLE_EXT;
+	return rte_intr_callback_register(&server_intr_handle,
+					  mlx5_pmd_socket_handle, NULL);
+}
+
+/**
+ * Uninstall interrupt handler.
+ */
+static void
+mlx5_pmd_interrupt_handler_uninstall(void)
+{
+	if (server_socket) {
+		mlx5_intr_callback_unregister(&server_intr_handle,
+					      mlx5_pmd_socket_handle,
+					      NULL);
+	}
+	server_intr_handle.fd = 0;
+	server_intr_handle.type = RTE_INTR_HANDLE_UNKNOWN; }
+
+/**
+ * Initialise the socket to communicate with the secondary process
+ *
+ * @param[in] dev
+ *   Pointer to Ethernet device.
+ *
+ * @return
+ *   0 on success, a negative value otherwise.
+ */
+int
+mlx5_pmd_socket_init(void)
+{
+	struct sockaddr_un sun = {
+		.sun_family = AF_UNIX,
+	};
+	int ret = -1;
+	int flags;
+
+	assert(rte_eal_process_type() == RTE_PROC_PRIMARY);
+	if (server_socket)
+		return 0;
+	/*
+	 * Initialize the socket to communicate with the secondary
+	 * process.
+	 */
+	ret = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (ret < 0) {
+		DRV_LOG(WARNING, "Failed to open mlx5 socket: %s",
+			strerror(errno));
+		goto error;
+	}
+	server_socket = ret;
+	flags = fcntl(server_socket, F_GETFL, 0);
+	if (flags == -1)
+		goto error;
+	ret = fcntl(server_socket, F_SETFL, flags | O_NONBLOCK);
+	if (ret < 0)
+		goto error;
+	mlx5_pmd_make_path(&sun, getpid());
+	remove(sun.sun_path);
+	ret = bind(server_socket, (const struct sockaddr *)&sun, sizeof(sun));
+	if (ret < 0) {
+		DRV_LOG(WARNING,
+			"cannot bind mlx5 socket: %s", strerror(errno));
+		goto close;
+	}
+	ret = listen(server_socket, 0);
+	if (ret < 0) {
+		DRV_LOG(WARNING, "cannot listen on mlx5 socket: %s",
+			strerror(errno));
+		goto close;
+	}
+	if (mlx5_pmd_interrupt_handler_install()) {
+		DRV_LOG(WARNING, "cannot register interrupt handler for

mlx5 socket: %s",

drivers/net/mlx5/mlx5_socket.c new file +17 -0
+			strerror(errno));
+		goto close;
+	}
+	return 0;
+close:
+	remove(sun.sun_path);
+error:
+	claim_zero(close(server_socket));
+	server_socket = 0;
+	DRV_LOG(ERR, "Cannot initialize socket: %s", strerror(errno));
+	return -errno;
+}
+
+/**
+ * Un-Initialize the pmd socket
+ */
+void __attribute__((destructor))
Quoted 8 lines
Quoted 7 lines
drivers/net/mlx5/mlx5_socket.c new file +6 -0
+mlx5_pmd_socket_uninit(void)
+{
+	if (!server_socket)
+		return;
+	mlx5_pmd_interrupt_handler_uninstall();
+	MKSTR(path, "/var/tmp/dpdk_%s_%d", MLX5_DRIVER_NAME,

getpid());

I would move the MKSTR a couple lines below, after the close() call.

OK, this seems make code more readable. I'll update it in v2.

Quoted 8 lines
Quoted 7 lines
drivers/net/mlx5/mlx5_socket.c new file +4 -1
+	claim_zero(close(server_socket));
+	server_socket = 0;
+	claim_zero(remove(path));
+}
--

2.24.1

With best regards, Slava

Thank you~
-Jack

author
#13 lore raw

PATCH 5/5 doc: update mlx5 document for flow dump feature

In reply to Xiaoyu Min

From: Xueming Li <[email protected]>

Guide of mlx5 is updated on how to dump HW flows.

---
 doc/guides/nics/mlx5.rst | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
doc/guides/nics/mlx5.rst +28 -0
index 92228d3cca..e6d09521e2 100644
@@ -1316,3 +1316,31 @@ ConnectX-4/ConnectX-5/ConnectX-6/BlueField devices managed by librte_pmd_mlx5.
       Port 3 Link Up - speed 10000 Mbps - full-duplex
       Done
       testpmd>
+
+HowTo dump flows
+----------------
+
+This section demonstrates how to dump flows. Currently, it's possible to dump
+all flows with assistence of external tools.
+
+#. 2 ways to get flow raw file:
+
+   - Using testpmd CLI:
+
+   .. code-block:: console
+
+       testpmd> flow dump <port> <output_file>
+
+   - call rte_flow_dev_dump api:
+
+   .. code-block:: console
+
+       rte_flow_dev_dump(port, file, NULL);
+
+#. Dump humanreadable flows from raw file:
+
+   Get flow parsing tool from: https://github.com/Mellanox/mlx_steering_dump
+
+   .. code-block:: console
+
+       mlx_steering_dump.py -f <output_file>
Signature · 1 line
2.24.1
author
#14 lore raw

v2 cover ethdev: add API to dump device internal flow info

In reply to Xiaoyu Min

This serial patchs is based on RFC [1] and the comments on it.

A new rte flow API is added to dump device internal representation information for the offloaded rte flows.

This is very helpful for user and developer to debug flow offloading stuff, i.e, to check whether PMD offloads the rte flow in a correct way from HW perspective.

The output file is in vendor specific format. An external vendor provided tool may be needed in order to get human readable info.

[1]: https://patches.dpdk.org/cover/64597/

v2:
* Bump MLX5 PMD glue version to 20.02.0
* MLX5 PMD validate port id
* Code improvement per review comments

Xiaoyu Min (2):
ethdev: add API to dump device internal flow info net/mlx5: support flow dump API

Xueming Li (3):
app/testpmd: new flow dump CLI
net/mlx5: add socket server for external tools
doc: update mlx5 document for flow dump feature

 app/test-pmd/cmdline_flow.c              |  91 +++++++++
 app/test-pmd/config.c                    |  27 +++
 app/test-pmd/testpmd.h                   |   1 +
 doc/guides/nics/mlx5.rst                 |  28 +++
 drivers/net/mlx5/Makefile                |   8 +-
 drivers/net/mlx5/meson.build             |   5 +-
 drivers/net/mlx5/mlx5.c                  |   2 +
 drivers/net/mlx5/mlx5.h                  |   9 +
 drivers/net/mlx5/mlx5_devx_cmds.c        |  35 ++++
 drivers/net/mlx5/mlx5_flow.c             |  24 +++
 drivers/net/mlx5/mlx5_glue.c             |  13 ++
 drivers/net/mlx5/mlx5_glue.h             |   1 +
 drivers/net/mlx5/mlx5_socket.c           | 231 +++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev_version.map |   3 +
 lib/librte_ethdev/rte_flow.c             |  16 ++
 lib/librte_ethdev/rte_flow.h             |  21 +++
 lib/librte_ethdev/rte_flow_driver.h      |   5 +
 17 files changed, 518 insertions(+), 2 deletions(-)
 create mode 100644 drivers/net/mlx5/mlx5_socket.c
Signature · 1 line
2.24.1
author
#15 lore raw

PATCH v2 1/5 ethdev: add API to dump device internal flow info

Introduce an API which dump the device's internal representation information of rte flows in hardware.

---
 lib/librte_ethdev/rte_ethdev_version.map |  3 +++
 lib/librte_ethdev/rte_flow.c             | 16 ++++++++++++++++
 lib/librte_ethdev/rte_flow.h             | 21 +++++++++++++++++++++
 lib/librte_ethdev/rte_flow_driver.h      |  5 +++++
 4 files changed, 45 insertions(+)
lib/librte_ethdev/rte_ethdev_version.map +3 -0
index a7dacf2cf2..3f32fdecf7 100644
@@ -227,4 +227,7 @@ EXPERIMENTAL {
 	rte_flow_dynf_metadata_mask;
 	rte_flow_dynf_metadata_register;
 	rte_eth_dev_set_ptypes;
+
+	# added in 20.02
+	rte_flow_dev_dump;
 };
lib/librte_ethdev/rte_flow.c +16 -0
index 87a3e8c4c6..751ce721b2 100644
@@ -1212,3 +1212,19 @@ rte_flow_expand_rss(struct rte_flow_expand_rss *buf, size_t size,
 	}
 	return lsize;
 }
+
+int
+rte_flow_dev_dump(uint16_t port_id, FILE *file, struct rte_flow_error *error)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
+
+	if (unlikely(!ops))
+		return -rte_errno;
+	if (likely(!!ops->dev_dump))
+		return flow_err(port_id, ops->dev_dump(dev, file, error),
+				error);
+	return rte_flow_error_set(error, ENOSYS,
+				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				  NULL, rte_strerror(ENOSYS));
+}
lib/librte_ethdev/rte_flow.h +21 -0
index 58b50265d2..cf7cf61ae8 100644
@@ -2790,6 +2790,27 @@ enum rte_flow_conv_op {
 	RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
 };
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump hardware internal representation information of
+ * rte flow to file.
+ *
+ * @param[in] port_id
+ *    The port identifier of the Ethernet device.
+ * @param[in] file
+ *   A pointer to a file for output.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL. PMDs initialize this
+ *   structure in case of error only.
+ * @return
+ *   0 on success, a nagative value otherwise.
+ */
+__rte_experimental
+int
+rte_flow_dev_dump(uint16_t port_id, FILE *file, struct rte_flow_error *error);
+
 /**
  * Check if mbuf dynamic field for metadata is registered.
  *
lib/librte_ethdev/rte_flow_driver.h +5 -0
index a0359853e6..51a9a57a0f 100644
@@ -96,6 +96,11 @@ struct rte_flow_ops {
 		(struct rte_eth_dev *,
 		 int,
 		 struct rte_flow_error *);
+	/** See rte_flow_dev_dump(). */
+	int (*dev_dump)
+		(struct rte_eth_dev *dev,
+		 FILE *file,
+		 struct rte_flow_error *error);
 };
 
 /**
Signature · 1 line
2.24.1
author
#17 lore raw

PATCH v2 2/5 net/mlx5: support flow dump API

In reply to Xiaoyu Min

Dump fdb/nic_rx/nic_tx raw flow data into specified file.

---
 drivers/net/mlx5/Makefile         |  7 ++++++-
 drivers/net/mlx5/meson.build      |  4 +++-
 drivers/net/mlx5/mlx5.h           |  4 ++++
 drivers/net/mlx5/mlx5_devx_cmds.c | 35 +++++++++++++++++++++++++++++++
 drivers/net/mlx5/mlx5_flow.c      | 24 +++++++++++++++++++++
 drivers/net/mlx5/mlx5_glue.c      | 13 ++++++++++++
 drivers/net/mlx5/mlx5_glue.h      |  1 +
 7 files changed, 86 insertions(+), 2 deletions(-)
drivers/net/mlx5/Makefile +6 -1
index c5cf4397ac..3e71516ea9 100644
@@ -8,7 +8,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
 LIB = librte_pmd_mlx5.a
 LIB_GLUE = $(LIB_GLUE_BASE).$(LIB_GLUE_VERSION)
 LIB_GLUE_BASE = librte_pmd_mlx5_glue.so
-LIB_GLUE_VERSION = 19.08.0
+LIB_GLUE_VERSION = 20.02.0
 
 # Sources.
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5.c
@@ -203,6 +203,11 @@ mlx5_autoconf.h.new: $(RTE_SDK)/buildtools/auto-config-h.sh
 		infiniband/mlx5dv.h \
 		func mlx5dv_dr_action_create_flow_meter \
 		$(AUTOCONF_OUTPUT)
+	$Q sh -- '$<' '$@' \
+		HAVE_MLX5_DR_FLOW_DUMP \
+		infiniband/mlx5dv.h \
+		func mlx5dv_dump_dr_domain \
+		$(AUTOCONF_OUTPUT)
 	$Q sh -- '$<' '$@' \
 		HAVE_MLX5DV_MMAP_GET_NC_PAGES_CMD \
 		infiniband/mlx5dv.h \
drivers/net/mlx5/meson.build +3 -1
index d6b32db794..b6736965e3 100644
@@ -11,7 +11,7 @@ build = true
 
 pmd_dlopen = (get_option('ibverbs_link') == 'dlopen')
 LIB_GLUE_BASE = 'librte_pmd_mlx5_glue.so'
-LIB_GLUE_VERSION = '19.08.0'
+LIB_GLUE_VERSION = '20.02.0'
 LIB_GLUE = LIB_GLUE_BASE + '.' + LIB_GLUE_VERSION
 if pmd_dlopen
 	dpdk_conf.set('RTE_IBVERBS_LINK_DLOPEN', 1)
@@ -186,6 +186,8 @@ if build
 		'RDMA_NLDEV_ATTR_PORT_INDEX' ],
 		[ 'HAVE_RDMA_NLDEV_ATTR_NDEV_INDEX', 'rdma/rdma_netlink.h',
 		'RDMA_NLDEV_ATTR_NDEV_INDEX' ],
+		[ 'HAVE_MLX5_DR_FLOW_DUMP', 'infiniband/mlx5dv.h',
+		'mlx5dv_dump_dr_domain'],
 	]
 	config = configuration_data()
 	foreach arg:has_sym_args
drivers/net/mlx5/mlx5.h +4 -0
index c3df8256ce..047181b32e 100644
@@ -975,6 +975,8 @@ struct mlx5_flow_counter *mlx5_counter_alloc(struct rte_eth_dev *dev);
 void mlx5_counter_free(struct rte_eth_dev *dev, struct mlx5_flow_counter *cnt);
 int mlx5_counter_query(struct rte_eth_dev *dev, struct mlx5_flow_counter *cnt,
 		       bool clear, uint64_t *pkts, uint64_t *bytes);
+int mlx5_flow_dev_dump(struct rte_eth_dev *dev, FILE *file,
+		       struct rte_flow_error *error);
 
 /* mlx5_mp.c */
 void mlx5_mp_req_start_rxtx(struct rte_eth_dev *dev);
@@ -1049,6 +1051,8 @@ struct mlx5_devx_obj *mlx5_devx_cmd_create_tis
 	(struct ibv_context *ctx, struct mlx5_devx_tis_attr *tis_attr);
 struct mlx5_devx_obj *mlx5_devx_cmd_create_td(struct ibv_context *ctx);
 
+int mlx5_devx_cmd_flow_dump(struct mlx5_ibv_shared *sh, FILE *file);
+
 /* mlx5_flow_meter.c */
 
 int mlx5_flow_meter_ops_get(struct rte_eth_dev *dev, void *arg);
drivers/net/mlx5/mlx5_devx_cmds.c +35 -0
index 9893287ba8..d6bf15689d 100644
@@ -927,3 +927,38 @@ mlx5_devx_cmd_create_td(struct ibv_context *ctx)
 			   transport_domain);
 	return td;
 }
+
+/**
+ * Dump all flows to file.
+ *
+ * @param[in] sh
+ *   Pointer to context.
+ * @param[out] file
+ *   Pointer to file stream.
+ *
+ * @return
+ *   0 on success, a nagative value otherwise.
+ */
+int
+mlx5_devx_cmd_flow_dump(struct mlx5_ibv_shared *sh __rte_unused,
+			FILE *file __rte_unused)
+{
+	int ret = 0;
+
+#ifdef HAVE_MLX5_DR_FLOW_DUMP
+	if (sh->fdb_domain) {
+		ret = mlx5_glue->dr_dump_domain(file, sh->fdb_domain);
+		if (ret)
+			return ret;
+	}
+	assert(sh->rx_domain);
+	ret = mlx5_glue->dr_dump_domain(file, sh->rx_domain);
+	if (ret)
+		return ret;
+	assert(sh->tx_domain);
+	ret = mlx5_glue->dr_dump_domain(file, sh->tx_domain);
+#else
+	ret = ENOTSUP;
+#endif
+	return -ret;
+}
drivers/net/mlx5/mlx5_flow.c +24 -0
index cb9d265f6f..0126cd8f92 100644
@@ -236,6 +236,7 @@ static const struct rte_flow_ops mlx5_flow_ops = {
 	.flush = mlx5_flow_flush,
 	.isolate = mlx5_flow_isolate,
 	.query = mlx5_flow_query,
+	.dev_dump = mlx5_flow_dev_dump,
 };
 
 /* Convert FDIR request to Generic flow. */
@@ -5679,3 +5680,26 @@ mlx5_flow_discover_mreg_c(struct rte_eth_dev *dev)
 		config->flow_mreg_c[n] = REG_NONE;
 	return 0;
 }
+
+/**
+ * Dump flow raw hw data to file
+ *
+ * @param[in] dev
+ *    The pointer to Ethernet device.
+ * @param[in] file
+ *   A pointer to a file for output.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL. PMDs initialize this
+ *   structure in case of error only.
+ * @return
+ *   0 on success, a nagative value otherwise.
+ */
+int
+mlx5_flow_dev_dump(struct rte_eth_dev *dev,
+		   FILE *file,
+		   struct rte_flow_error *error __rte_unused)
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+
+	return mlx5_devx_cmd_flow_dump(priv->sh, file);
+}
drivers/net/mlx5/mlx5_glue.c +13 -0
index 0917bf28d6..4906eebc01 100644
@@ -1037,6 +1037,18 @@ mlx5_glue_devx_port_query(struct ibv_context *ctx,
 #endif
 }
 
+static int
+mlx5_glue_dr_dump_domain(FILE *file, void *domain)
+{
+#ifdef HAVE_MLX5_DR_FLOW_DUMP
+	return mlx5dv_dump_dr_domain(file, domain);
+#else
+	RTE_SET_USED(file);
+	RTE_SET_USED(domain);
+	return -ENOTSUP;
+#endif
+}
+
 alignas(RTE_CACHE_LINE_SIZE)
 const struct mlx5_glue *mlx5_glue = &(const struct mlx5_glue){
 	.version = MLX5_GLUE_VERSION,
@@ -1134,4 +1146,5 @@ const struct mlx5_glue *mlx5_glue = &(const struct mlx5_glue){
 	.devx_umem_dereg = mlx5_glue_devx_umem_dereg,
 	.devx_qp_query = mlx5_glue_devx_qp_query,
 	.devx_port_query = mlx5_glue_devx_port_query,
+	.dr_dump_domain = mlx5_glue_dr_dump_domain,
 };
drivers/net/mlx5/mlx5_glue.h +1 -0
index 6442f1eba8..6771a18c64 100644
@@ -256,6 +256,7 @@ struct mlx5_glue {
 	int (*devx_port_query)(struct ibv_context *ctx,
 			       uint32_t port_num,
 			       struct mlx5dv_devx_port *mlx5_devx_port);
+	int (*dr_dump_domain)(FILE *file, void *domain);
 };
 
 const struct mlx5_glue *mlx5_glue;
Signature · 1 line
2.24.1
author
#18 lore raw

PATCH v2 3/5 app/testpmd: new flow dump CLI

In reply to Xiaoyu Min

From: Xueming Li <[email protected]>

New flow dump CLI to dump device internal representation information of flows into screen.

---
 app/test-pmd/cmdline_flow.c | 91 +++++++++++++++++++++++++++++++++++++
 app/test-pmd/config.c       | 27 +++++++++++
 app/test-pmd/testpmd.h      |  1 +
 3 files changed, 119 insertions(+)
app/test-pmd/cmdline_flow.c +91 -0
index 99dade7d8c..19336e5d42 100644
@@ -41,6 +41,7 @@ enum index {
 	BOOLEAN,
 	STRING,
 	HEX,
+	FILE_PATH,
 	MAC_ADDR,
 	IPV4_ADDR,
 	IPV6_ADDR,
@@ -63,6 +64,7 @@ enum index {
 	CREATE,
 	DESTROY,
 	FLUSH,
+	DUMP,
 	QUERY,
 	LIST,
 	ISOLATE,
@@ -631,6 +633,9 @@ struct buffer {
 			uint32_t *rule;
 			uint32_t rule_n;
 		} destroy; /**< Destroy arguments. */
+		struct {
+			char file[128];
+		} dump; /**< Dump arguments. */
 		struct {
 			uint32_t rule;
 			struct rte_flow_action action;
@@ -685,6 +690,12 @@ static const enum index next_destroy_attr[] = {
 	ZERO,
 };
 
+static const enum index next_dump_attr[] = {
+	FILE_PATH,
+	END,
+	ZERO,
+};
+
 static const enum index next_list_attr[] = {
 	LIST_GROUP,
 	END,
@@ -1374,6 +1385,9 @@ static int parse_destroy(struct context *, const struct token *,
 static int parse_flush(struct context *, const struct token *,
 		       const char *, unsigned int,
 		       void *, unsigned int);
+static int parse_dump(struct context *, const struct token *,
+		      const char *, unsigned int,
+		      void *, unsigned int);
 static int parse_query(struct context *, const struct token *,
 		       const char *, unsigned int,
 		       void *, unsigned int);
@@ -1401,6 +1415,9 @@ static int parse_string(struct context *, const struct token *,
 static int parse_hex(struct context *ctx, const struct token *token,
 			const char *str, unsigned int len,
 			void *buf, unsigned int size);
+static int parse_string0(struct context *, const struct token *,
+			const char *, unsigned int,
+			void *, unsigned int);
 static int parse_mac_addr(struct context *, const struct token *,
 			  const char *, unsigned int,
 			  void *, unsigned int);
@@ -1494,6 +1511,12 @@ static const struct token token_list[] = {
 		.type = "HEX",
 		.help = "fixed string",
 		.call = parse_hex,
+	},
+	[FILE_PATH] = {
+		.name = "{file path}",
+		.type = "STRING",
+		.help = "file path",
+		.call = parse_string0,
 		.comp = comp_none,
 	},
 	[MAC_ADDR] = {
@@ -1555,6 +1578,7 @@ static const struct token token_list[] = {
 			      CREATE,
 			      DESTROY,
 			      FLUSH,
+			      DUMP,
 			      LIST,
 			      QUERY,
 			      ISOLATE)),
@@ -1589,6 +1613,14 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
 		.call = parse_flush,
 	},
+	[DUMP] = {
+		.name = "dump",
+		.help = "dump all flow rules to file",
+		.next = NEXT(next_dump_attr, NEXT_ENTRY(PORT_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.dump.file),
+			     ARGS_ENTRY(struct buffer, port)),
+		.call = parse_dump,
+	},
 	[QUERY] = {
 		.name = "query",
 		.help = "query an existing flow rule",
@@ -5012,6 +5044,33 @@ parse_flush(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse tokens for dump command. */
+static int
+parse_dump(struct context *ctx, const struct token *token,
+	    const char *str, unsigned int len,
+	    void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+
+	/* Token name must match. */
+	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+		return -1;
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return len;
+	if (!out->command) {
+		if (ctx->curr != DUMP)
+			return -1;
+		if (sizeof(*out) > size)
+			return -1;
+		out->command = ctx->curr;
+		ctx->objdata = 0;
+		ctx->object = out;
+		ctx->objmask = NULL;
+	}
+	return len;
+}
+
 /** Parse tokens for query command. */
 static int
 parse_query(struct context *ctx, const struct token *token,
@@ -5409,6 +5468,35 @@ parse_hex(struct context *ctx, const struct token *token,
 
 }
 
+/**
+ * Parse a zero-ended string.
+ */
+static int
+parse_string0(struct context *ctx, const struct token *token __rte_unused,
+	     const char *str, unsigned int len,
+	     void *buf, unsigned int size)
+{
+	const struct arg *arg_data = pop_args(ctx);
+
+	/* Arguments are expected. */
+	if (!arg_data)
+		return -1;
+	size = arg_data->size;
+	/* Bit-mask fill is not supported. */
+	if (arg_data->mask || size < len + 1)
+		goto error;
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg_data->offset;
+	strncpy(buf, str, len);
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg_data->offset, 0xff, len);
+	return len;
+error:
+	push_args(ctx, arg_data);
+	return -1;
+}
+
 /**
  * Parse a MAC address.
  *
@@ -6068,6 +6156,9 @@ cmd_flow_parsed(const struct buffer *in)
 	case FLUSH:
 		port_flow_flush(in->port);
 		break;
+	case DUMP:
+		port_flow_dump(in->port, in->args.dump.file);
+		break;
 	case QUERY:
 		port_flow_query(in->port, in->args.query.rule,
 				&in->args.query.action);
app/test-pmd/config.c +27 -0
index 9da1ffb034..1b4bdf7bf3 100644
@@ -1441,6 +1441,33 @@ port_flow_flush(portid_t port_id)
 	return ret;
 }
 
+/** Dump all flow rules. */
+int
+port_flow_dump(portid_t port_id, const char *file_name)
+{
+	int ret = 0;
+	FILE *file = stdout;
+	struct rte_flow_error error;
+
+	if (file_name && strlen(file_name)) {
+		file = fopen(file_name, "w");
+		if (!file) {
+			printf("Failed to create file %s: %s\n", file_name,
+			       strerror(errno));
+			return -errno;
+		}
+	}
+	ret = rte_flow_dev_dump(port_id, file, &error);
+	if (ret) {
+		port_flow_complain(&error);
+		printf("Failed to dump flow: %s\n", strerror(-ret));
+	} else
+		printf("Flow dump finished\n");
+	if (file_name && strlen(file_name))
+		fclose(file);
+	return ret;
+}
+
 /** Query a flow rule. */
 int
 port_flow_query(portid_t port_id, uint32_t rule,
app/test-pmd/testpmd.h +1 -0
index 857a11f8de..e1b9aba360 100644
@@ -734,6 +734,7 @@ int port_flow_create(portid_t port_id,
 		     const struct rte_flow_action *actions);
 int port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule);
 int port_flow_flush(portid_t port_id);
+int port_flow_dump(portid_t port_id, const char *file_name);
 int port_flow_query(portid_t port_id, uint32_t rule,
 		    const struct rte_flow_action *action);
 void port_flow_list(portid_t port_id, uint32_t n, const uint32_t *group);
Signature · 1 line
2.24.1
author
#19 lore raw

PATCH v2 4/5 net/mlx5: add socket server for external tools

In reply to Xiaoyu Min

From: Xueming Li <[email protected]>

Add pmd unix socket server to enable external tool applications to trigger flow dump.

Socket path:

	/var/tmp/dpdk_mlx5_<pid>

Socket format:
io_raw: port_id of uint16
file: file descriptor of int

---
 drivers/net/mlx5/Makefile      |   1 +
 drivers/net/mlx5/meson.build   |   1 +
 drivers/net/mlx5/mlx5.c        |   2 +
 drivers/net/mlx5/mlx5.h        |   5 +
 drivers/net/mlx5/mlx5_socket.c | 231 +++++++++++++++++++++++++++++++++
 5 files changed, 240 insertions(+)
 create mode 100644 drivers/net/mlx5/mlx5_socket.c
drivers/net/mlx5/Makefile +1 -0
index 3e71516ea9..0466d9d00c 100644
@@ -39,6 +39,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_mp.c
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_nl.c
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_devx_cmds.c
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_utils.c
+SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_socket.c
 
 ifeq ($(CONFIG_RTE_IBVERBS_LINK_DLOPEN),y)
 INSTALL-$(CONFIG_RTE_LIBRTE_MLX5_PMD)-lib += $(LIB_GLUE)
drivers/net/mlx5/meson.build +1 -0
index b6736965e3..3ad4f02a53 100644
@@ -61,6 +61,7 @@ if build
 		'mlx5_vlan.c',
 		'mlx5_devx_cmds.c',
 		'mlx5_utils.c',
+		'mlx5_socket.c',
 	)
 	if (dpdk_conf.has('RTE_ARCH_X86_64')
 		or dpdk_conf.has('RTE_ARCH_ARM64')
drivers/net/mlx5/mlx5.c +2 -0
index 50960c91ce..ffee39c1a0 100644
@@ -2954,6 +2954,8 @@ mlx5_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
 	struct mlx5_dev_config dev_config;
 	int ret;
 
+	if (rte_eal_process_type() == RTE_PROC_PRIMARY)
+		mlx5_pmd_socket_init();
 	ret = mlx5_init_once();
 	if (ret) {
 		DRV_LOG(ERR, "unable to init PMD global data: %s",
drivers/net/mlx5/mlx5.h +5 -0
index 047181b32e..ceb6de821e 100644
@@ -990,6 +990,11 @@ void mlx5_mp_uninit_primary(void);
 int mlx5_mp_init_secondary(void);
 void mlx5_mp_uninit_secondary(void);
 
+/* mlx5_socket.c */
+
+int mlx5_pmd_socket_init(void);
+void mlx5_pmd_socket_uninit(void);
+
 /* mlx5_nl.c */
 
 int mlx5_nl_init(int protocol);
drivers/net/mlx5/mlx5_socket.c new file +231 -0

231 lines not on this page · raw message

Signature · 1 line
2.24.1
author
#20 lore raw

PATCH v2 5/5 doc: update mlx5 document for flow dump feature

In reply to Xiaoyu Min

From: Xueming Li <[email protected]>

Guide of mlx5 is updated on how to dump HW flows.

---
 doc/guides/nics/mlx5.rst | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
doc/guides/nics/mlx5.rst +28 -0
index 92228d3cca..e6d09521e2 100644
@@ -1316,3 +1316,31 @@ ConnectX-4/ConnectX-5/ConnectX-6/BlueField devices managed by librte_pmd_mlx5.
       Port 3 Link Up - speed 10000 Mbps - full-duplex
       Done
       testpmd>
+
+HowTo dump flows
+----------------
+
+This section demonstrates how to dump flows. Currently, it's possible to dump
+all flows with assistence of external tools.
+
+#. 2 ways to get flow raw file:
+
+   - Using testpmd CLI:
+
+   .. code-block:: console
+
+       testpmd> flow dump <port> <output_file>
+
+   - call rte_flow_dev_dump api:
+
+   .. code-block:: console
+
+       rte_flow_dev_dump(port, file, NULL);
+
+#. Dump humanreadable flows from raw file:
+
+   Get flow parsing tool from: https://github.com/Mellanox/mlx_steering_dump
+
+   .. code-block:: console
+
+       mlx_steering_dump.py -f <output_file>
Signature · 1 line
2.24.1
#21 lore raw
In reply to Xiaoyu Min
Quoted 19 lines · Xiaoyu Min

This serial patchs is based on RFC [1] and the comments on it.

A new rte flow API is added to dump device internal representation information for the offloaded rte flows.

This is very helpful for user and developer to debug flow offloading stuff, i.e, to check whether PMD offloads the rte flow in a correct way from HW perspective.

The output file is in vendor specific format. An external vendor provided tool may be needed in order to get human readable info.

[1]: https://patches.dpdk.org/cover/64597/

v2:
* Bump MLX5 PMD glue version to 20.02.0
* MLX5 PMD validate port id
* Code improvement per review comments

Quoted 8 lines

Xiaoyu Min (2):
ethdev: add API to dump device internal flow info net/mlx5: support flow dump API

Xueming Li (3):
app/testpmd: new flow dump CLI
net/mlx5: add socket server for external tools
doc: update mlx5 document for flow dump feature

Series applied to dpdk-next-net/master, thanks.

#22 lore raw

17/01/2020 20:26, Ferruh Yigit:

Quoted 11 lines · Xiaoyu Min
Quoted 8 lines

Xiaoyu Min (2):
ethdev: add API to dump device internal flow info net/mlx5: support flow dump API

Xueming Li (3):
app/testpmd: new flow dump CLI
net/mlx5: add socket server for external tools
doc: update mlx5 document for flow dump feature

Series applied to dpdk-next-net/master, thanks.

I think we are missing an update of the testpmd doc.

author
#23 lore raw
Quoted 6 lines · Thomas Monjalon

17/01/2020 20:26, Ferruh Yigit:

Quoted 5 lines · Xiaoyu Min
Quoted 4 lines

Xiaoyu Min (2):
ethdev: add API to dump device internal flow info net/mlx5: support flow dump API

Quoted 9 lines
Quoted 6 lines
Quoted 4 lines

Xueming Li (3):
app/testpmd: new flow dump CLI
net/mlx5: add socket server for external tools
doc: update mlx5 document for flow dump feature

Series applied to dpdk-next-net/master, thanks.

I think we are missing an update of the testpmd doc.

Yes, you are right. I'll update testpmd doc.

-Jack