DPDK patches and discussions
 help / color / mirror / Atom feed
* [PATCH 1/2] net/ice:  add Tx scheduling tree dump support
@ 2023-12-26 18:54 Qi Zhang
  2023-12-26 18:54 ` [PATCH 2/2] doc: add document for diagnostic utilities Qi Zhang
                   ` (8 more replies)
  0 siblings, 9 replies; 22+ messages in thread
From: Qi Zhang @ 2023-12-26 18:54 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Added Testpmd CLI support for dumping Tx scheduling tree.

Usage:
testpmd>txsched dump <port_id> <brief|detail> <filename>

The output file is in "dot" format, which can be converted
into an image file using Graphviz.

- In "brief" mode, all scheduling nodes in the tree are displayed.
- In "detail" mode, each node's configuration parameters are also
  displayed.

Renamed `ice_ddp_package.c` to `ice_diagnose.c`, which now contains
all CLI support for diagnostic purposes.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
 .../ice/{ice_ddp_package.c => ice_diagnose.c} | 361 ++++++++++++++++++
 drivers/net/ice/ice_ethdev.h                  |   3 +
 drivers/net/ice/ice_testpmd.c                 |  78 ++++
 drivers/net/ice/meson.build                   |   2 +-
 drivers/net/ice/version.map                   |   1 +
 5 files changed, 444 insertions(+), 1 deletion(-)
 rename drivers/net/ice/{ice_ddp_package.c => ice_diagnose.c} (61%)

diff --git a/drivers/net/ice/ice_ddp_package.c b/drivers/net/ice/ice_diagnose.c
similarity index 61%
rename from drivers/net/ice/ice_ddp_package.c
rename to drivers/net/ice/ice_diagnose.c
index 0aa19eb282..1968cf7039 100644
--- a/drivers/net/ice/ice_ddp_package.c
+++ b/drivers/net/ice/ice_diagnose.c
@@ -11,6 +11,7 @@
 #include <rte_tailq.h>
 
 #include "ice_ethdev.h"
+#include "ice_rxtx.h"
 
 #define ICE_BLK_MAX_COUNT          512
 #define ICE_BUFF_SEG_HEADER_FLAG   0x1
@@ -507,3 +508,363 @@ int rte_pmd_ice_dump_switch(uint16_t port, uint8_t **buff, uint32_t *size)
 
 	return ice_dump_switch(dev, buff, size);
 }
+
+static void print_rl_profile(struct ice_aqc_rl_profile_elem *prof,
+			     FILE *stream)
+{
+	fprintf(stream, "\t\t\t\t\t<td>\n");
+	fprintf(stream, "\t\t\t\t\t\t<table>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>id</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->profile_id);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>max burst size</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->max_burst_size);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>rate limit multiply</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->rl_multiply);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>wake up calculation</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->wake_up_calc);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>rate limit encode</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->rl_encode);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t</table>\n");
+	fprintf(stream, "\t\t\t\t\t</td>\n");
+}
+
+static
+void print_elem_type(FILE *stream, u8 type)
+{
+	switch (type) {
+	case 1:
+		fprintf(stream, "root");
+		break;
+	case 2:
+		fprintf(stream, "tc");
+		break;
+	case 3:
+		fprintf(stream, "se_generic");
+		break;
+	case 4:
+		fprintf(stream, "entry_point");
+		break;
+	case 5:
+		fprintf(stream, "leaf");
+		break;
+	default:
+		fprintf(stream, "%d", type);
+		break;
+	}
+}
+
+static
+void print_valid_sections(FILE *stream, u8 vs)
+{
+	if ((vs & 0x1) != 0)
+		fprintf(stream, "generic ");
+	if ((vs & 0x2) != 0)
+		fprintf(stream, "cir ");
+	if ((vs & 0x4) != 0)
+		fprintf(stream, "eir ");
+	if ((vs & 0x8) != 0)
+		fprintf(stream, "shared ");
+}
+
+static
+void print_scheduling_mode(FILE *stream, bool flag)
+{
+	if (flag)
+		fprintf(stream, "pps");
+	else
+		fprintf(stream, "bps");
+}
+
+static
+void print_priority_mode(FILE *stream, bool flag)
+{
+	if (flag)
+		fprintf(stream, "single priority node");
+	else
+		fprintf(stream, "wfq");
+}
+
+static
+void print_node(struct ice_aqc_txsched_elem_data *data,
+		struct ice_aqc_rl_profile_elem *cir_prof,
+		struct ice_aqc_rl_profile_elem *eir_prof,
+		struct ice_aqc_rl_profile_elem *shared_prof,
+		bool detail, FILE *stream)
+{
+	if (!detail)
+		goto edge_only;
+
+	fprintf(stream, "\tNODE_%d [\n", data->node_teid);
+	fprintf(stream, "\t\tlabel=<\n");
+
+	fprintf(stream, "\t\t\t<table>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> teid </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->node_teid);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> type </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_elem_type(stream, data->data.elem_type);
+	fprintf(stream, "</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> valid sections </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_valid_sections(stream, data->data.valid_sections);
+	fprintf(stream,	"</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> scheduling mode </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_scheduling_mode(stream, (data->data.generic & 0x1) != 0);
+	fprintf(stream, "</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> priority </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", (data->data.generic >> 1) & 0x7);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> priority mode</td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_priority_mode(stream, ((data->data.generic >> 4) & 0x1) != 0);
+	fprintf(stream,	"</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> adjustment value </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", (data->data.generic >> 5) & 0x3);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> suspended </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.flags & 0x1);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> cir bw profile </td>\n");
+	if (cir_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(cir_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> cir bw weight </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.cir_bw.bw_alloc);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> eir bw profile </td>\n");
+	if (eir_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(eir_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> eir bw weight </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.eir_bw.bw_alloc);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> shared rl profile </td>\n");
+	if (shared_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(shared_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t</table>\n");
+
+	fprintf(stream, "\t\t>\n");
+	fprintf(stream, "\t\tshape=plain\n");
+	fprintf(stream, "\t]\n");
+
+edge_only:
+	if (data->parent_teid != 0xFFFFFFFF)
+		fprintf(stream, "\tNODE_%d -> NODE_%d\n", data->parent_teid, data->node_teid);
+}
+
+static
+int query_rl_profile(struct ice_hw *hw,
+		     uint8_t level, uint8_t flags, uint16_t profile_id,
+		     struct ice_aqc_rl_profile_elem *data)
+{
+	enum ice_status ice_status;
+
+	data->level = level;
+	data->flags = flags;
+	data->profile_id = profile_id;
+
+	ice_status = ice_aq_query_rl_profile(hw, 1, data,
+					     sizeof(struct ice_aqc_rl_profile_elem), NULL);
+
+	if (ice_status != ICE_SUCCESS) {
+		PMD_DRV_LOG(ERR, "Failed to query rl profile.");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static
+int query_node(struct ice_hw *hw, uint32_t child, uint32_t *parent,
+	       uint8_t level, bool detail, FILE *stream)
+{
+	struct ice_aqc_txsched_elem_data data;
+	enum ice_status status;
+	struct ice_aqc_rl_profile_elem cir_prof;
+	struct ice_aqc_rl_profile_elem eir_prof;
+	struct ice_aqc_rl_profile_elem shared_prof;
+	struct ice_aqc_rl_profile_elem *cp = NULL;
+	struct ice_aqc_rl_profile_elem *ep = NULL;
+	struct ice_aqc_rl_profile_elem *sp = NULL;
+	int ret;
+
+	(void)level;
+	status = ice_sched_query_elem(hw, child, &data);
+	if (status != ICE_SUCCESS) {
+		PMD_DRV_LOG(ERR, "Failed to query scheduling node.");
+		return -EINVAL;
+	}
+
+	*parent = data.parent_teid;
+
+	if (data.data.cir_bw.bw_profile_idx != 0) {
+		ret = query_rl_profile(hw, level, 0, data.data.cir_bw.bw_profile_idx, &cir_prof);
+
+		if (ret)
+			return ret;
+		cp = &cir_prof;
+	}
+
+	if (data.data.eir_bw.bw_profile_idx != 0) {
+		ret = query_rl_profile(hw, level, 0, data.data.eir_bw.bw_profile_idx, &eir_prof);
+
+		if (ret)
+			return ret;
+		ep = &eir_prof;
+	}
+
+	if (data.data.srl_id != 0) {
+		ret = query_rl_profile(hw, level, 0, data.data.srl_id, &shared_prof);
+
+		if (ret)
+			return ret;
+		sp = &shared_prof;
+	}
+
+	print_node(&data, cp, ep, sp, detail, stream);
+
+	return 0;
+}
+
+static
+int query_nodes(struct ice_hw *hw,
+		uint32_t *children, int child_num,
+		uint32_t *parents, int *parent_num,
+		uint8_t level, bool detail,
+		FILE *stream)
+{
+	uint32_t parent;
+	int i;
+	int j;
+
+	*parent_num = 0;
+	for (i = 0; i < child_num; i++) {
+		bool exist = false;
+		int ret;
+
+		ret = query_node(hw, children[i], &parent, level, detail, stream);
+		if (ret)
+			return -EINVAL;
+
+		for (j = 0; j < *parent_num; j++) {
+			if (parents[j] == parent) {
+				exist = true;
+				break;
+			}
+		}
+
+		if (!exist && parent != 0xFFFFFFFF)
+			parents[(*parent_num)++] = parent;
+	}
+
+	return 0;
+}
+
+int rte_pmd_ice_dump_txsched(uint16_t port, bool detail, FILE *stream)
+{
+	struct rte_eth_dev *dev;
+	struct ice_hw *hw;
+	struct ice_pf *pf;
+	struct ice_q_ctx *q_ctx;
+	uint16_t q_num;
+	uint16_t i;
+	struct ice_tx_queue *txq;
+	uint32_t buf1[256];
+	uint32_t buf2[256];
+	uint32_t *children = buf1;
+	uint32_t *parents = buf2;
+	int child_num = 0;
+	int parent_num = 0;
+	uint8_t level;
+
+	dev = &rte_eth_devices[port];
+	hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	pf = ICE_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	level = hw->num_tx_sched_layers;
+
+	q_num = dev->data->nb_tx_queues;
+
+	/* main vsi */
+	for (i = 0; i < q_num; i++) {
+		txq = dev->data->tx_queues[i];
+		q_ctx = ice_get_lan_q_ctx(hw, txq->vsi->idx, 0, i);
+		children[child_num++] = q_ctx->q_teid;
+	}
+
+	/* fdir vsi */
+	q_ctx = ice_get_lan_q_ctx(hw, pf->fdir.fdir_vsi->idx, 0, 0);
+	children[child_num++] = q_ctx->q_teid;
+
+	fprintf(stream, "digraph tx_sched {\n");
+	while (child_num > 0) {
+		int ret;
+		ret = query_nodes(hw, children, child_num,
+				  parents, &parent_num,
+				  level, detail, stream);
+		if (ret)
+			return ret;
+
+		children = parents;
+		child_num = parent_num;
+		level--;
+	}
+	fprintf(stream, "}\n");
+
+	return 0;
+}
diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index d607f028e0..1338c80d14 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -739,4 +739,7 @@ int rte_pmd_ice_dump_package(uint16_t port, uint8_t **buff, uint32_t *size);
 
 __rte_experimental
 int rte_pmd_ice_dump_switch(uint16_t port, uint8_t **buff, uint32_t *size);
+
+__rte_experimental
+int rte_pmd_ice_dump_txsched(uint16_t port, bool detail, FILE *stream);
 #endif /* _ICE_ETHDEV_H_ */
diff --git a/drivers/net/ice/ice_testpmd.c b/drivers/net/ice/ice_testpmd.c
index a7a8d0c53c..941dd3419a 100644
--- a/drivers/net/ice/ice_testpmd.c
+++ b/drivers/net/ice/ice_testpmd.c
@@ -3,6 +3,7 @@
  */
 
 #include <stdlib.h>
+#include <stdio.h>
 
 #include <cmdline_parse_num.h>
 #include <cmdline_parse_string.h>
@@ -148,6 +149,76 @@ cmdline_parse_inst_t cmd_ddp_dump_switch = {
 	},
 };
 
+/* Dump Tx Scheduling Tree configuration, only for ice PF */
+struct cmd_txsched_dump_result {
+	cmdline_fixed_string_t txsched;
+	cmdline_fixed_string_t dump;
+	portid_t port_id;
+	cmdline_fixed_string_t mode;
+	char filepath[];
+};
+
+cmdline_parse_token_string_t cmd_txsched_dump_txsched =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, txsched, "txsched");
+cmdline_parse_token_string_t cmd_txsched_dump_dump =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, dump, "dump");
+cmdline_parse_token_num_t cmd_txsched_dump_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_txsched_dump_result, port_id, RTE_UINT16);
+cmdline_parse_token_string_t cmd_txsched_dump_mode =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, mode, "brief#detail");
+cmdline_parse_token_string_t cmd_txsched_dump_filepath =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, filepath, NULL);
+
+static void
+cmd_txsched_dump_parsed(void *parsed_result,
+			__rte_unused struct cmdline *cl,
+			__rte_unused void *data)
+{
+	struct cmd_txsched_dump_result *res = parsed_result;
+	struct rte_eth_dev *dev;
+	bool detail = false;
+	FILE *fp;
+
+	if (!rte_eth_dev_is_valid_port(res->port_id)) {
+		fprintf(stderr, "invalid port id %d\n", res->port_id);
+		return;
+	}
+
+	dev = &rte_eth_devices[res->port_id];
+	if (!is_ice_supported(dev)) {
+		fprintf(stderr,	"Device doesn't support "
+				"dump tx scheduling runtime configure.\n");
+		return;
+	}
+
+	if (!strcmp(res->mode, "detail"))
+		detail = true;
+
+	fp = fopen(res->filepath, "w");
+	if (fp == NULL) {
+		fprintf(stderr, "Failed to open file\n");
+		return;
+	}
+
+	if (rte_pmd_ice_dump_txsched(res->port_id, detail, fp))
+		fprintf(stderr, "Failed to dump Tx scheduring runtime configure.\n");
+	fclose(fp);
+}
+
+cmdline_parse_inst_t cmd_txsched_dump = {
+	.f = cmd_txsched_dump_parsed,
+	.data = NULL,
+	.help_str = "txsched dump <port_id> <brief|detail> <config_path>",
+	.tokens = {
+		(void *)&cmd_txsched_dump_txsched,
+		(void *)&cmd_txsched_dump_dump,
+		(void *)&cmd_txsched_dump_port_id,
+		(void *)&cmd_txsched_dump_mode,
+		(void *)&cmd_txsched_dump_filepath,
+		NULL,
+	},
+};
+
 static struct testpmd_driver_commands ice_cmds = {
 	.commands = {
 	{
@@ -161,8 +232,15 @@ static struct testpmd_driver_commands ice_cmds = {
 		"ddp dump switch (port_id) (config_path)\n"
 		"    Dump a runtime switch configure on a port\n\n",
 
+	},
+	{
+		&cmd_txsched_dump,
+		"txsched dump (port_id) <brief|detail> (config_path)\n"
+		"    Dump tx scheduling runtime configure on a port\n\n",
+
 	},
 	{ NULL, NULL },
 	},
 };
+
 TESTPMD_ADD_DRIVER_COMMANDS(ice_cmds)
diff --git a/drivers/net/ice/meson.build b/drivers/net/ice/meson.build
index a957fc5d3a..b7f2188e62 100644
--- a/drivers/net/ice/meson.build
+++ b/drivers/net/ice/meson.build
@@ -6,7 +6,7 @@ objs = [base_objs]
 
 sources = files(
         'ice_acl_filter.c',
-        'ice_ddp_package.c',
+        'ice_diagnose.c',
         'ice_ethdev.c',
         'ice_fdir_filter.c',
         'ice_generic_flow.c',
diff --git a/drivers/net/ice/version.map b/drivers/net/ice/version.map
index 4e924c8f4d..24b425d6f7 100644
--- a/drivers/net/ice/version.map
+++ b/drivers/net/ice/version.map
@@ -8,4 +8,5 @@ EXPERIMENTAL {
 	# added in 19.11
 	rte_pmd_ice_dump_package;
 	rte_pmd_ice_dump_switch;
+	rte_pmd_ice_dump_txsched;
 };
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH 2/2] doc: add document for diagnostic utilities
  2023-12-26 18:54 [PATCH 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
@ 2023-12-26 18:54 ` Qi Zhang
  2023-12-27 12:31 ` [PATCH v2 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 22+ messages in thread
From: Qi Zhang @ 2023-12-26 18:54 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Document CLI for diagnose purpose.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
 doc/guides/nics/ice.rst | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
index 820a385b06..29309abe4d 100644
--- a/doc/guides/nics/ice.rst
+++ b/doc/guides/nics/ice.rst
@@ -411,6 +411,42 @@ To start ``testpmd``, and add vlan 10 to port 0:
 
     testpmd> rx_vlan add 10 0
 
+Diagnostic Utilities
+--------------------
+
+Dump DDP Package
+~~~~~~~~~~~~~~~~
+
+Dump the runtime packet processing pipeline configuration into a
+binary file. This helps the support team diagnose hardware
+configuration issues.
+
+Usage::
+
+    testpmd>ddp dump <port_id> <output_file>
+
+Dump Switch Configurations
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Dump detail hardware configurations related to the switch pipeline
+stage into a binary file.
+
+Usage::
+
+    testpmd>ddp dump switch <port_id> <output_file>
+
+Dump Tx Scheduling Tree
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Dump the runtime Tx scheduling tree into a DOT file.
+
+Usage::
+
+    testpmd>txsched dump <port_id> <brief|detail> <output_file>
+
+In "brief" mode, all scheduling nodes in the tree are displayed.
+In "detail" mode, each node's configuration parameters are also displayed.
+
 Limitations or Known issues
 ---------------------------
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 1/2] net/ice:  add Tx scheduling tree dump support
  2023-12-26 18:54 [PATCH 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
  2023-12-26 18:54 ` [PATCH 2/2] doc: add document for diagnostic utilities Qi Zhang
@ 2023-12-27 12:31 ` Qi Zhang
  2023-12-27 12:31   ` [PATCH v2 2/2] doc: add document for diagnostic utilities Qi Zhang
  2023-12-28 10:10 ` [PATCH v3 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 22+ messages in thread
From: Qi Zhang @ 2023-12-27 12:31 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Added Testpmd CLI support for dumping Tx scheduling tree.

Usage:
testpmd>txsched dump <port_id> <brief|detail> <filename>

The output file is in "dot" format, which can be converted
into an image file using Graphviz.

- In "brief" mode, all scheduling nodes in the tree are displayed.
- In "detail" mode, each node's configuration parameters are also
  displayed.

Renamed `ice_ddp_package.c` to `ice_diagnose.c`, which now contains
all CLI support for diagnostic purposes.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---

v2:
- fix patchwork build issue.

 .../ice/{ice_ddp_package.c => ice_diagnose.c} | 367 ++++++++++++++++++
 drivers/net/ice/ice_ethdev.h                  |   3 +
 drivers/net/ice/ice_testpmd.c                 |  65 ++++
 drivers/net/ice/meson.build                   |   2 +-
 drivers/net/ice/version.map                   |   1 +
 5 files changed, 437 insertions(+), 1 deletion(-)
 rename drivers/net/ice/{ice_ddp_package.c => ice_diagnose.c} (61%)

diff --git a/drivers/net/ice/ice_ddp_package.c b/drivers/net/ice/ice_diagnose.c
similarity index 61%
rename from drivers/net/ice/ice_ddp_package.c
rename to drivers/net/ice/ice_diagnose.c
index 0aa19eb282..42459accfa 100644
--- a/drivers/net/ice/ice_ddp_package.c
+++ b/drivers/net/ice/ice_diagnose.c
@@ -11,6 +11,7 @@
 #include <rte_tailq.h>
 
 #include "ice_ethdev.h"
+#include "ice_rxtx.h"
 
 #define ICE_BLK_MAX_COUNT          512
 #define ICE_BUFF_SEG_HEADER_FLAG   0x1
@@ -507,3 +508,369 @@ int rte_pmd_ice_dump_switch(uint16_t port, uint8_t **buff, uint32_t *size)
 
 	return ice_dump_switch(dev, buff, size);
 }
+
+static void print_rl_profile(struct ice_aqc_rl_profile_elem *prof,
+			     FILE *stream)
+{
+	fprintf(stream, "\t\t\t\t\t<td>\n");
+	fprintf(stream, "\t\t\t\t\t\t<table>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>id</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->profile_id);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>max burst size</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->max_burst_size);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>rate limit multiply</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->rl_multiply);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>wake up calculation</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->wake_up_calc);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>rate limit encode</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->rl_encode);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t</table>\n");
+	fprintf(stream, "\t\t\t\t\t</td>\n");
+}
+
+static
+void print_elem_type(FILE *stream, u8 type)
+{
+	switch (type) {
+	case 1:
+		fprintf(stream, "root");
+		break;
+	case 2:
+		fprintf(stream, "tc");
+		break;
+	case 3:
+		fprintf(stream, "se_generic");
+		break;
+	case 4:
+		fprintf(stream, "entry_point");
+		break;
+	case 5:
+		fprintf(stream, "leaf");
+		break;
+	default:
+		fprintf(stream, "%d", type);
+		break;
+	}
+}
+
+static
+void print_valid_sections(FILE *stream, u8 vs)
+{
+	if ((vs & 0x1) != 0)
+		fprintf(stream, "generic ");
+	if ((vs & 0x2) != 0)
+		fprintf(stream, "cir ");
+	if ((vs & 0x4) != 0)
+		fprintf(stream, "eir ");
+	if ((vs & 0x8) != 0)
+		fprintf(stream, "shared ");
+}
+
+static
+void print_scheduling_mode(FILE *stream, bool flag)
+{
+	if (flag)
+		fprintf(stream, "pps");
+	else
+		fprintf(stream, "bps");
+}
+
+static
+void print_priority_mode(FILE *stream, bool flag)
+{
+	if (flag)
+		fprintf(stream, "single priority node");
+	else
+		fprintf(stream, "wfq");
+}
+
+static
+void print_node(struct ice_aqc_txsched_elem_data *data,
+		struct ice_aqc_rl_profile_elem *cir_prof,
+		struct ice_aqc_rl_profile_elem *eir_prof,
+		struct ice_aqc_rl_profile_elem *shared_prof,
+		bool detail, FILE *stream)
+{
+	if (!detail)
+		goto edge_only;
+
+	fprintf(stream, "\tNODE_%d [\n", data->node_teid);
+	fprintf(stream, "\t\tlabel=<\n");
+
+	fprintf(stream, "\t\t\t<table>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> teid </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->node_teid);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> type </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_elem_type(stream, data->data.elem_type);
+	fprintf(stream, "</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> valid sections </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_valid_sections(stream, data->data.valid_sections);
+	fprintf(stream,	"</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> scheduling mode </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_scheduling_mode(stream, (data->data.generic & 0x1) != 0);
+	fprintf(stream, "</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> priority </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", (data->data.generic >> 1) & 0x7);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> priority mode</td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_priority_mode(stream, ((data->data.generic >> 4) & 0x1) != 0);
+	fprintf(stream,	"</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> adjustment value </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", (data->data.generic >> 5) & 0x3);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> suspended </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.flags & 0x1);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> cir bw profile </td>\n");
+	if (cir_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(cir_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> cir bw weight </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.cir_bw.bw_alloc);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> eir bw profile </td>\n");
+	if (eir_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(eir_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> eir bw weight </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.eir_bw.bw_alloc);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> shared rl profile </td>\n");
+	if (shared_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(shared_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t</table>\n");
+
+	fprintf(stream, "\t\t>\n");
+	fprintf(stream, "\t\tshape=plain\n");
+	fprintf(stream, "\t]\n");
+
+edge_only:
+	if (data->parent_teid != 0xFFFFFFFF)
+		fprintf(stream, "\tNODE_%d -> NODE_%d\n", data->parent_teid, data->node_teid);
+}
+
+static
+int query_rl_profile(struct ice_hw *hw,
+		     uint8_t level, uint8_t flags, uint16_t profile_id,
+		     struct ice_aqc_rl_profile_elem *data)
+{
+	enum ice_status ice_status;
+
+	data->level = level;
+	data->flags = flags;
+	data->profile_id = profile_id;
+
+	ice_status = ice_aq_query_rl_profile(hw, 1, data,
+					     sizeof(struct ice_aqc_rl_profile_elem), NULL);
+
+	if (ice_status != ICE_SUCCESS) {
+		PMD_DRV_LOG(ERR, "Failed to query rl profile.");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static
+int query_node(struct ice_hw *hw, uint32_t child, uint32_t *parent,
+	       uint8_t level, bool detail, FILE *stream)
+{
+	struct ice_aqc_txsched_elem_data data;
+	enum ice_status status;
+	struct ice_aqc_rl_profile_elem cir_prof;
+	struct ice_aqc_rl_profile_elem eir_prof;
+	struct ice_aqc_rl_profile_elem shared_prof;
+	struct ice_aqc_rl_profile_elem *cp = NULL;
+	struct ice_aqc_rl_profile_elem *ep = NULL;
+	struct ice_aqc_rl_profile_elem *sp = NULL;
+	int ret;
+
+	(void)level;
+	status = ice_sched_query_elem(hw, child, &data);
+	if (status != ICE_SUCCESS) {
+		PMD_DRV_LOG(ERR, "Failed to query scheduling node.");
+		return -EINVAL;
+	}
+
+	*parent = data.parent_teid;
+
+	if (data.data.cir_bw.bw_profile_idx != 0) {
+		ret = query_rl_profile(hw, level, 0, data.data.cir_bw.bw_profile_idx, &cir_prof);
+
+		if (ret)
+			return ret;
+		cp = &cir_prof;
+	}
+
+	if (data.data.eir_bw.bw_profile_idx != 0) {
+		ret = query_rl_profile(hw, level, 0, data.data.eir_bw.bw_profile_idx, &eir_prof);
+
+		if (ret)
+			return ret;
+		ep = &eir_prof;
+	}
+
+	if (data.data.srl_id != 0) {
+		ret = query_rl_profile(hw, level, 0, data.data.srl_id, &shared_prof);
+
+		if (ret)
+			return ret;
+		sp = &shared_prof;
+	}
+
+	print_node(&data, cp, ep, sp, detail, stream);
+
+	return 0;
+}
+
+static
+int query_nodes(struct ice_hw *hw,
+		uint32_t *children, int child_num,
+		uint32_t *parents, int *parent_num,
+		uint8_t level, bool detail,
+		FILE *stream)
+{
+	uint32_t parent;
+	int i;
+	int j;
+
+	*parent_num = 0;
+	for (i = 0; i < child_num; i++) {
+		bool exist = false;
+		int ret;
+
+		ret = query_node(hw, children[i], &parent, level, detail, stream);
+		if (ret)
+			return -EINVAL;
+
+		for (j = 0; j < *parent_num; j++) {
+			if (parents[j] == parent) {
+				exist = true;
+				break;
+			}
+		}
+
+		if (!exist && parent != 0xFFFFFFFF)
+			parents[(*parent_num)++] = parent;
+	}
+
+	return 0;
+}
+
+int rte_pmd_ice_dump_txsched(uint16_t port, bool detail, FILE *stream)
+{
+	struct rte_eth_dev *dev;
+	struct ice_hw *hw;
+	struct ice_pf *pf;
+	struct ice_q_ctx *q_ctx;
+	uint16_t q_num;
+	uint16_t i;
+	struct ice_tx_queue *txq;
+	uint32_t buf1[256];
+	uint32_t buf2[256];
+	uint32_t *children = buf1;
+	uint32_t *parents = buf2;
+	int child_num = 0;
+	int parent_num = 0;
+	uint8_t level;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+	if (!is_ice_supported(dev))
+		return -ENOTSUP;
+
+	dev = &rte_eth_devices[port];
+	hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	pf = ICE_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	level = hw->num_tx_sched_layers;
+
+	q_num = dev->data->nb_tx_queues;
+
+	/* main vsi */
+	for (i = 0; i < q_num; i++) {
+		txq = dev->data->tx_queues[i];
+		q_ctx = ice_get_lan_q_ctx(hw, txq->vsi->idx, 0, i);
+		children[child_num++] = q_ctx->q_teid;
+	}
+
+	/* fdir vsi */
+	q_ctx = ice_get_lan_q_ctx(hw, pf->fdir.fdir_vsi->idx, 0, 0);
+	children[child_num++] = q_ctx->q_teid;
+
+	fprintf(stream, "digraph tx_sched {\n");
+	while (child_num > 0) {
+		int ret;
+		ret = query_nodes(hw, children, child_num,
+				  parents, &parent_num,
+				  level, detail, stream);
+		if (ret)
+			return ret;
+
+		children = parents;
+		child_num = parent_num;
+		level--;
+	}
+	fprintf(stream, "}\n");
+
+	return 0;
+}
diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index d607f028e0..1338c80d14 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -739,4 +739,7 @@ int rte_pmd_ice_dump_package(uint16_t port, uint8_t **buff, uint32_t *size);
 
 __rte_experimental
 int rte_pmd_ice_dump_switch(uint16_t port, uint8_t **buff, uint32_t *size);
+
+__rte_experimental
+int rte_pmd_ice_dump_txsched(uint16_t port, bool detail, FILE *stream);
 #endif /* _ICE_ETHDEV_H_ */
diff --git a/drivers/net/ice/ice_testpmd.c b/drivers/net/ice/ice_testpmd.c
index a7a8d0c53c..98c02d68c6 100644
--- a/drivers/net/ice/ice_testpmd.c
+++ b/drivers/net/ice/ice_testpmd.c
@@ -3,6 +3,7 @@
  */
 
 #include <stdlib.h>
+#include <stdio.h>
 
 #include <cmdline_parse_num.h>
 #include <cmdline_parse_string.h>
@@ -148,6 +149,63 @@ cmdline_parse_inst_t cmd_ddp_dump_switch = {
 	},
 };
 
+/* Dump Tx Scheduling Tree configuration, only for ice PF */
+struct cmd_txsched_dump_result {
+	cmdline_fixed_string_t txsched;
+	cmdline_fixed_string_t dump;
+	portid_t port_id;
+	cmdline_fixed_string_t mode;
+	char filepath[];
+};
+
+cmdline_parse_token_string_t cmd_txsched_dump_txsched =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, txsched, "txsched");
+cmdline_parse_token_string_t cmd_txsched_dump_dump =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, dump, "dump");
+cmdline_parse_token_num_t cmd_txsched_dump_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_txsched_dump_result, port_id, RTE_UINT16);
+cmdline_parse_token_string_t cmd_txsched_dump_mode =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, mode, "brief#detail");
+cmdline_parse_token_string_t cmd_txsched_dump_filepath =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, filepath, NULL);
+
+static void
+cmd_txsched_dump_parsed(void *parsed_result,
+			__rte_unused struct cmdline *cl,
+			__rte_unused void *data)
+{
+	struct cmd_txsched_dump_result *res = parsed_result;
+	bool detail = false;
+	FILE *fp;
+
+	if (!strcmp(res->mode, "detail"))
+		detail = true;
+
+	fp = fopen(res->filepath, "w");
+	if (fp == NULL) {
+		fprintf(stderr, "Failed to open file\n");
+		return;
+	}
+
+	if (rte_pmd_ice_dump_txsched(res->port_id, detail, fp))
+		fprintf(stderr, "Failed to dump Tx scheduring runtime configure.\n");
+	fclose(fp);
+}
+
+cmdline_parse_inst_t cmd_txsched_dump = {
+	.f = cmd_txsched_dump_parsed,
+	.data = NULL,
+	.help_str = "txsched dump <port_id> <brief|detail> <config_path>",
+	.tokens = {
+		(void *)&cmd_txsched_dump_txsched,
+		(void *)&cmd_txsched_dump_dump,
+		(void *)&cmd_txsched_dump_port_id,
+		(void *)&cmd_txsched_dump_mode,
+		(void *)&cmd_txsched_dump_filepath,
+		NULL,
+	},
+};
+
 static struct testpmd_driver_commands ice_cmds = {
 	.commands = {
 	{
@@ -161,8 +219,15 @@ static struct testpmd_driver_commands ice_cmds = {
 		"ddp dump switch (port_id) (config_path)\n"
 		"    Dump a runtime switch configure on a port\n\n",
 
+	},
+	{
+		&cmd_txsched_dump,
+		"txsched dump (port_id) <brief|detail> (config_path)\n"
+		"    Dump tx scheduling runtime configure on a port\n\n",
+
 	},
 	{ NULL, NULL },
 	},
 };
+
 TESTPMD_ADD_DRIVER_COMMANDS(ice_cmds)
diff --git a/drivers/net/ice/meson.build b/drivers/net/ice/meson.build
index a957fc5d3a..b7f2188e62 100644
--- a/drivers/net/ice/meson.build
+++ b/drivers/net/ice/meson.build
@@ -6,7 +6,7 @@ objs = [base_objs]
 
 sources = files(
         'ice_acl_filter.c',
-        'ice_ddp_package.c',
+        'ice_diagnose.c',
         'ice_ethdev.c',
         'ice_fdir_filter.c',
         'ice_generic_flow.c',
diff --git a/drivers/net/ice/version.map b/drivers/net/ice/version.map
index 4e924c8f4d..24b425d6f7 100644
--- a/drivers/net/ice/version.map
+++ b/drivers/net/ice/version.map
@@ -8,4 +8,5 @@ EXPERIMENTAL {
 	# added in 19.11
 	rte_pmd_ice_dump_package;
 	rte_pmd_ice_dump_switch;
+	rte_pmd_ice_dump_txsched;
 };
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 2/2] doc: add document for diagnostic utilities
  2023-12-27 12:31 ` [PATCH v2 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
@ 2023-12-27 12:31   ` Qi Zhang
  0 siblings, 0 replies; 22+ messages in thread
From: Qi Zhang @ 2023-12-27 12:31 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Document CLI for diagnose purpose.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
 doc/guides/nics/ice.rst | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
index 820a385b06..29309abe4d 100644
--- a/doc/guides/nics/ice.rst
+++ b/doc/guides/nics/ice.rst
@@ -411,6 +411,42 @@ To start ``testpmd``, and add vlan 10 to port 0:
 
     testpmd> rx_vlan add 10 0
 
+Diagnostic Utilities
+--------------------
+
+Dump DDP Package
+~~~~~~~~~~~~~~~~
+
+Dump the runtime packet processing pipeline configuration into a
+binary file. This helps the support team diagnose hardware
+configuration issues.
+
+Usage::
+
+    testpmd>ddp dump <port_id> <output_file>
+
+Dump Switch Configurations
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Dump detail hardware configurations related to the switch pipeline
+stage into a binary file.
+
+Usage::
+
+    testpmd>ddp dump switch <port_id> <output_file>
+
+Dump Tx Scheduling Tree
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Dump the runtime Tx scheduling tree into a DOT file.
+
+Usage::
+
+    testpmd>txsched dump <port_id> <brief|detail> <output_file>
+
+In "brief" mode, all scheduling nodes in the tree are displayed.
+In "detail" mode, each node's configuration parameters are also displayed.
+
 Limitations or Known issues
 ---------------------------
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v3 1/2] net/ice:  add Tx scheduling tree dump support
  2023-12-26 18:54 [PATCH 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
  2023-12-26 18:54 ` [PATCH 2/2] doc: add document for diagnostic utilities Qi Zhang
  2023-12-27 12:31 ` [PATCH v2 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
@ 2023-12-28 10:10 ` Qi Zhang
  2023-12-28 10:10   ` [PATCH v3 2/2] doc: add document for diagnostic utilities Qi Zhang
  2024-01-02 10:28 ` [PATCH v4 1/2] net/ice: reset Tx sched node during commit Qi Zhang
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 22+ messages in thread
From: Qi Zhang @ 2023-12-28 10:10 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Added Testpmd CLI support for dumping Tx scheduling tree.

Usage:
testpmd>txsched dump <port_id> <brief|detail> <filename>

The output file is in "dot" format, which can be converted
into an image file using Graphviz.

- In "brief" mode, all scheduling nodes in the tree are displayed.
- In "detail" mode, each node's configuration parameters are also
  displayed.

Renamed `ice_ddp_package.c` to `ice_diagnose.c`, which now contains
all CLI support for diagnostic purposes.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---

v3:
- fix incorrect parameter when query rl profile

v2:
- fix CI build issue

 .../ice/{ice_ddp_package.c => ice_diagnose.c} | 367 ++++++++++++++++++
 drivers/net/ice/ice_ethdev.h                  |   3 +
 drivers/net/ice/ice_testpmd.c                 |  65 ++++
 drivers/net/ice/meson.build                   |   2 +-
 drivers/net/ice/version.map                   |   1 +
 5 files changed, 437 insertions(+), 1 deletion(-)
 rename drivers/net/ice/{ice_ddp_package.c => ice_diagnose.c} (61%)

diff --git a/drivers/net/ice/ice_ddp_package.c b/drivers/net/ice/ice_diagnose.c
similarity index 61%
rename from drivers/net/ice/ice_ddp_package.c
rename to drivers/net/ice/ice_diagnose.c
index 0aa19eb282..e54554fc9c 100644
--- a/drivers/net/ice/ice_ddp_package.c
+++ b/drivers/net/ice/ice_diagnose.c
@@ -11,6 +11,7 @@
 #include <rte_tailq.h>
 
 #include "ice_ethdev.h"
+#include "ice_rxtx.h"
 
 #define ICE_BLK_MAX_COUNT          512
 #define ICE_BUFF_SEG_HEADER_FLAG   0x1
@@ -507,3 +508,369 @@ int rte_pmd_ice_dump_switch(uint16_t port, uint8_t **buff, uint32_t *size)
 
 	return ice_dump_switch(dev, buff, size);
 }
+
+static void print_rl_profile(struct ice_aqc_rl_profile_elem *prof,
+			     FILE *stream)
+{
+	fprintf(stream, "\t\t\t\t\t<td>\n");
+	fprintf(stream, "\t\t\t\t\t\t<table>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>id</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->profile_id);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>max burst size</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->max_burst_size);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>rate limit multiply</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->rl_multiply);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>wake up calculation</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->wake_up_calc);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>rate limit encode</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->rl_encode);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t</table>\n");
+	fprintf(stream, "\t\t\t\t\t</td>\n");
+}
+
+static
+void print_elem_type(FILE *stream, u8 type)
+{
+	switch (type) {
+	case 1:
+		fprintf(stream, "root");
+		break;
+	case 2:
+		fprintf(stream, "tc");
+		break;
+	case 3:
+		fprintf(stream, "se_generic");
+		break;
+	case 4:
+		fprintf(stream, "entry_point");
+		break;
+	case 5:
+		fprintf(stream, "leaf");
+		break;
+	default:
+		fprintf(stream, "%d", type);
+		break;
+	}
+}
+
+static
+void print_valid_sections(FILE *stream, u8 vs)
+{
+	if ((vs & 0x1) != 0)
+		fprintf(stream, "generic ");
+	if ((vs & 0x2) != 0)
+		fprintf(stream, "cir ");
+	if ((vs & 0x4) != 0)
+		fprintf(stream, "eir ");
+	if ((vs & 0x8) != 0)
+		fprintf(stream, "shared ");
+}
+
+static
+void print_scheduling_mode(FILE *stream, bool flag)
+{
+	if (flag)
+		fprintf(stream, "pps");
+	else
+		fprintf(stream, "bps");
+}
+
+static
+void print_priority_mode(FILE *stream, bool flag)
+{
+	if (flag)
+		fprintf(stream, "single priority node");
+	else
+		fprintf(stream, "wfq");
+}
+
+static
+void print_node(struct ice_aqc_txsched_elem_data *data,
+		struct ice_aqc_rl_profile_elem *cir_prof,
+		struct ice_aqc_rl_profile_elem *eir_prof,
+		struct ice_aqc_rl_profile_elem *shared_prof,
+		bool detail, FILE *stream)
+{
+	if (!detail)
+		goto edge_only;
+
+	fprintf(stream, "\tNODE_%d [\n", data->node_teid);
+	fprintf(stream, "\t\tlabel=<\n");
+
+	fprintf(stream, "\t\t\t<table>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> teid </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->node_teid);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> type </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_elem_type(stream, data->data.elem_type);
+	fprintf(stream, "</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> valid sections </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_valid_sections(stream, data->data.valid_sections);
+	fprintf(stream,	"</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> scheduling mode </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_scheduling_mode(stream, (data->data.generic & 0x1) != 0);
+	fprintf(stream, "</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> priority </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", (data->data.generic >> 1) & 0x7);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> priority mode</td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_priority_mode(stream, ((data->data.generic >> 4) & 0x1) != 0);
+	fprintf(stream,	"</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> adjustment value </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", (data->data.generic >> 5) & 0x3);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> suspended </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.flags & 0x1);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> cir bw profile </td>\n");
+	if (cir_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(cir_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> cir bw weight </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.cir_bw.bw_alloc);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> eir bw profile </td>\n");
+	if (eir_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(eir_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> eir bw weight </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.eir_bw.bw_alloc);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> shared rl profile </td>\n");
+	if (shared_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(shared_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t</table>\n");
+
+	fprintf(stream, "\t\t>\n");
+	fprintf(stream, "\t\tshape=plain\n");
+	fprintf(stream, "\t]\n");
+
+edge_only:
+	if (data->parent_teid != 0xFFFFFFFF)
+		fprintf(stream, "\tNODE_%d -> NODE_%d\n", data->parent_teid, data->node_teid);
+}
+
+static
+int query_rl_profile(struct ice_hw *hw,
+		     uint8_t level, uint8_t flags, uint16_t profile_id,
+		     struct ice_aqc_rl_profile_elem *data)
+{
+	enum ice_status ice_status;
+
+	data->level = level;
+	data->flags = flags;
+	data->profile_id = profile_id;
+
+	ice_status = ice_aq_query_rl_profile(hw, 1, data,
+					     sizeof(struct ice_aqc_rl_profile_elem), NULL);
+
+	if (ice_status != ICE_SUCCESS) {
+		PMD_DRV_LOG(ERR, "Failed to query rl profile.");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static
+int query_node(struct ice_hw *hw, uint32_t child, uint32_t *parent,
+	       uint8_t level, bool detail, FILE *stream)
+{
+	struct ice_aqc_txsched_elem_data data;
+	enum ice_status status;
+	struct ice_aqc_rl_profile_elem cir_prof;
+	struct ice_aqc_rl_profile_elem eir_prof;
+	struct ice_aqc_rl_profile_elem shared_prof;
+	struct ice_aqc_rl_profile_elem *cp = NULL;
+	struct ice_aqc_rl_profile_elem *ep = NULL;
+	struct ice_aqc_rl_profile_elem *sp = NULL;
+	int ret;
+
+	(void)level;
+	status = ice_sched_query_elem(hw, child, &data);
+	if (status != ICE_SUCCESS) {
+		PMD_DRV_LOG(ERR, "Failed to query scheduling node.");
+		return -EINVAL;
+	}
+
+	*parent = data.parent_teid;
+
+	if (data.data.cir_bw.bw_profile_idx != 0) {
+		ret = query_rl_profile(hw, level, 0, data.data.cir_bw.bw_profile_idx, &cir_prof);
+
+		if (ret)
+			return ret;
+		cp = &cir_prof;
+	}
+
+	if (data.data.eir_bw.bw_profile_idx != 0) {
+		ret = query_rl_profile(hw, level, 1, data.data.eir_bw.bw_profile_idx, &eir_prof);
+
+		if (ret)
+			return ret;
+		ep = &eir_prof;
+	}
+
+	if (data.data.srl_id != 0) {
+		ret = query_rl_profile(hw, level, 2, data.data.srl_id, &shared_prof);
+
+		if (ret)
+			return ret;
+		sp = &shared_prof;
+	}
+
+	print_node(&data, cp, ep, sp, detail, stream);
+
+	return 0;
+}
+
+static
+int query_nodes(struct ice_hw *hw,
+		uint32_t *children, int child_num,
+		uint32_t *parents, int *parent_num,
+		uint8_t level, bool detail,
+		FILE *stream)
+{
+	uint32_t parent;
+	int i;
+	int j;
+
+	*parent_num = 0;
+	for (i = 0; i < child_num; i++) {
+		bool exist = false;
+		int ret;
+
+		ret = query_node(hw, children[i], &parent, level, detail, stream);
+		if (ret)
+			return -EINVAL;
+
+		for (j = 0; j < *parent_num; j++) {
+			if (parents[j] == parent) {
+				exist = true;
+				break;
+			}
+		}
+
+		if (!exist && parent != 0xFFFFFFFF)
+			parents[(*parent_num)++] = parent;
+	}
+
+	return 0;
+}
+
+int rte_pmd_ice_dump_txsched(uint16_t port, bool detail, FILE *stream)
+{
+	struct rte_eth_dev *dev;
+	struct ice_hw *hw;
+	struct ice_pf *pf;
+	struct ice_q_ctx *q_ctx;
+	uint16_t q_num;
+	uint16_t i;
+	struct ice_tx_queue *txq;
+	uint32_t buf1[256];
+	uint32_t buf2[256];
+	uint32_t *children = buf1;
+	uint32_t *parents = buf2;
+	int child_num = 0;
+	int parent_num = 0;
+	uint8_t level;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+	if (!is_ice_supported(dev))
+		return -ENOTSUP;
+
+	dev = &rte_eth_devices[port];
+	hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	pf = ICE_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	level = hw->num_tx_sched_layers;
+
+	q_num = dev->data->nb_tx_queues;
+
+	/* main vsi */
+	for (i = 0; i < q_num; i++) {
+		txq = dev->data->tx_queues[i];
+		q_ctx = ice_get_lan_q_ctx(hw, txq->vsi->idx, 0, i);
+		children[child_num++] = q_ctx->q_teid;
+	}
+
+	/* fdir vsi */
+	q_ctx = ice_get_lan_q_ctx(hw, pf->fdir.fdir_vsi->idx, 0, 0);
+	children[child_num++] = q_ctx->q_teid;
+
+	fprintf(stream, "digraph tx_sched {\n");
+	while (child_num > 0) {
+		int ret;
+		ret = query_nodes(hw, children, child_num,
+				  parents, &parent_num,
+				  level, detail, stream);
+		if (ret)
+			return ret;
+
+		children = parents;
+		child_num = parent_num;
+		level--;
+	}
+	fprintf(stream, "}\n");
+
+	return 0;
+}
diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index d607f028e0..1338c80d14 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -739,4 +739,7 @@ int rte_pmd_ice_dump_package(uint16_t port, uint8_t **buff, uint32_t *size);
 
 __rte_experimental
 int rte_pmd_ice_dump_switch(uint16_t port, uint8_t **buff, uint32_t *size);
+
+__rte_experimental
+int rte_pmd_ice_dump_txsched(uint16_t port, bool detail, FILE *stream);
 #endif /* _ICE_ETHDEV_H_ */
diff --git a/drivers/net/ice/ice_testpmd.c b/drivers/net/ice/ice_testpmd.c
index a7a8d0c53c..98c02d68c6 100644
--- a/drivers/net/ice/ice_testpmd.c
+++ b/drivers/net/ice/ice_testpmd.c
@@ -3,6 +3,7 @@
  */
 
 #include <stdlib.h>
+#include <stdio.h>
 
 #include <cmdline_parse_num.h>
 #include <cmdline_parse_string.h>
@@ -148,6 +149,63 @@ cmdline_parse_inst_t cmd_ddp_dump_switch = {
 	},
 };
 
+/* Dump Tx Scheduling Tree configuration, only for ice PF */
+struct cmd_txsched_dump_result {
+	cmdline_fixed_string_t txsched;
+	cmdline_fixed_string_t dump;
+	portid_t port_id;
+	cmdline_fixed_string_t mode;
+	char filepath[];
+};
+
+cmdline_parse_token_string_t cmd_txsched_dump_txsched =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, txsched, "txsched");
+cmdline_parse_token_string_t cmd_txsched_dump_dump =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, dump, "dump");
+cmdline_parse_token_num_t cmd_txsched_dump_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_txsched_dump_result, port_id, RTE_UINT16);
+cmdline_parse_token_string_t cmd_txsched_dump_mode =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, mode, "brief#detail");
+cmdline_parse_token_string_t cmd_txsched_dump_filepath =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, filepath, NULL);
+
+static void
+cmd_txsched_dump_parsed(void *parsed_result,
+			__rte_unused struct cmdline *cl,
+			__rte_unused void *data)
+{
+	struct cmd_txsched_dump_result *res = parsed_result;
+	bool detail = false;
+	FILE *fp;
+
+	if (!strcmp(res->mode, "detail"))
+		detail = true;
+
+	fp = fopen(res->filepath, "w");
+	if (fp == NULL) {
+		fprintf(stderr, "Failed to open file\n");
+		return;
+	}
+
+	if (rte_pmd_ice_dump_txsched(res->port_id, detail, fp))
+		fprintf(stderr, "Failed to dump Tx scheduring runtime configure.\n");
+	fclose(fp);
+}
+
+cmdline_parse_inst_t cmd_txsched_dump = {
+	.f = cmd_txsched_dump_parsed,
+	.data = NULL,
+	.help_str = "txsched dump <port_id> <brief|detail> <config_path>",
+	.tokens = {
+		(void *)&cmd_txsched_dump_txsched,
+		(void *)&cmd_txsched_dump_dump,
+		(void *)&cmd_txsched_dump_port_id,
+		(void *)&cmd_txsched_dump_mode,
+		(void *)&cmd_txsched_dump_filepath,
+		NULL,
+	},
+};
+
 static struct testpmd_driver_commands ice_cmds = {
 	.commands = {
 	{
@@ -161,8 +219,15 @@ static struct testpmd_driver_commands ice_cmds = {
 		"ddp dump switch (port_id) (config_path)\n"
 		"    Dump a runtime switch configure on a port\n\n",
 
+	},
+	{
+		&cmd_txsched_dump,
+		"txsched dump (port_id) <brief|detail> (config_path)\n"
+		"    Dump tx scheduling runtime configure on a port\n\n",
+
 	},
 	{ NULL, NULL },
 	},
 };
+
 TESTPMD_ADD_DRIVER_COMMANDS(ice_cmds)
diff --git a/drivers/net/ice/meson.build b/drivers/net/ice/meson.build
index a957fc5d3a..b7f2188e62 100644
--- a/drivers/net/ice/meson.build
+++ b/drivers/net/ice/meson.build
@@ -6,7 +6,7 @@ objs = [base_objs]
 
 sources = files(
         'ice_acl_filter.c',
-        'ice_ddp_package.c',
+        'ice_diagnose.c',
         'ice_ethdev.c',
         'ice_fdir_filter.c',
         'ice_generic_flow.c',
diff --git a/drivers/net/ice/version.map b/drivers/net/ice/version.map
index 4e924c8f4d..24b425d6f7 100644
--- a/drivers/net/ice/version.map
+++ b/drivers/net/ice/version.map
@@ -8,4 +8,5 @@ EXPERIMENTAL {
 	# added in 19.11
 	rte_pmd_ice_dump_package;
 	rte_pmd_ice_dump_switch;
+	rte_pmd_ice_dump_txsched;
 };
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v3 2/2] doc: add document for diagnostic utilities
  2023-12-28 10:10 ` [PATCH v3 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
@ 2023-12-28 10:10   ` Qi Zhang
  0 siblings, 0 replies; 22+ messages in thread
From: Qi Zhang @ 2023-12-28 10:10 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Document CLI for diagnose purpose.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
 doc/guides/nics/ice.rst | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
index 820a385b06..29309abe4d 100644
--- a/doc/guides/nics/ice.rst
+++ b/doc/guides/nics/ice.rst
@@ -411,6 +411,42 @@ To start ``testpmd``, and add vlan 10 to port 0:
 
     testpmd> rx_vlan add 10 0
 
+Diagnostic Utilities
+--------------------
+
+Dump DDP Package
+~~~~~~~~~~~~~~~~
+
+Dump the runtime packet processing pipeline configuration into a
+binary file. This helps the support team diagnose hardware
+configuration issues.
+
+Usage::
+
+    testpmd>ddp dump <port_id> <output_file>
+
+Dump Switch Configurations
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Dump detail hardware configurations related to the switch pipeline
+stage into a binary file.
+
+Usage::
+
+    testpmd>ddp dump switch <port_id> <output_file>
+
+Dump Tx Scheduling Tree
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Dump the runtime Tx scheduling tree into a DOT file.
+
+Usage::
+
+    testpmd>txsched dump <port_id> <brief|detail> <output_file>
+
+In "brief" mode, all scheduling nodes in the tree are displayed.
+In "detail" mode, each node's configuration parameters are also displayed.
+
 Limitations or Known issues
 ---------------------------
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* RE: [PATCH v7 1/2] net/ice:  add Tx scheduling tree dump support
  2024-01-02 12:39 ` [PATCH v7 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
@ 2024-01-02  6:22   ` Wu, Wenjun1
  2024-01-02 12:39   ` [PATCH v7 2/2] doc: add document for ice diagnostic utilities Qi Zhang
  1 sibling, 0 replies; 22+ messages in thread
From: Wu, Wenjun1 @ 2024-01-02  6:22 UTC (permalink / raw)
  To: Zhang, Qi Z, Yang, Qiming; +Cc: dev

> -----Original Message-----
> From: Zhang, Qi Z <qi.z.zhang@intel.com>
> Sent: Tuesday, January 2, 2024 8:40 PM
> To: Yang, Qiming <qiming.yang@intel.com>; Wu, Wenjun1
> <wenjun1.wu@intel.com>
> Cc: dev@dpdk.org; Zhang, Qi Z <qi.z.zhang@intel.com>
> Subject: [PATCH v7 1/2] net/ice: add Tx scheduling tree dump support
> 
> Added Testpmd CLI support for dumping Tx scheduling tree.
> 
> Usage:
> testpmd>txsched dump <port_id> <brief|detail> <filename>
> 
> The output file is in "dot" format, which can be converted into an image file
> using Graphviz.
> 
> - In "brief" mode, all scheduling nodes in the tree are displayed.
> - In "detail" mode, each node's configuration parameters are also
>   displayed.
> 
> Renamed `ice_ddp_package.c` to `ice_diagnose.c`, which now contains all CLI
> support for diagnostic purposes.
> 
> Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
> ---

Acked-by: Wenjun Wu <wenjun1.wu@intel.com>


^ permalink raw reply	[flat|nested] 22+ messages in thread

* RE: [PATCH v7 2/2] doc: add document for ice diagnostic utilities
  2024-01-02 12:39   ` [PATCH v7 2/2] doc: add document for ice diagnostic utilities Qi Zhang
@ 2024-01-02  7:14     ` Yang, Qiming
  0 siblings, 0 replies; 22+ messages in thread
From: Yang, Qiming @ 2024-01-02  7:14 UTC (permalink / raw)
  To: Zhang, Qi Z, Wu, Wenjun1; +Cc: dev



> -----Original Message-----
> From: Zhang, Qi Z <qi.z.zhang@intel.com>
> Sent: Tuesday, January 2, 2024 8:40 PM
> To: Yang, Qiming <qiming.yang@intel.com>; Wu, Wenjun1
> <wenjun1.wu@intel.com>
> Cc: dev@dpdk.org; Zhang, Qi Z <qi.z.zhang@intel.com>
> Subject: [PATCH v7 2/2] doc: add document for ice diagnostic utilities
> 
> Document ice specific testpmd CLI for diagnose purpose.
> 
> Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
> ---
> v6:
> - fix title
> 
>  doc/guides/nics/ice.rst | 36 ++++++++++++++++++++++++++++++++++++
>  1 file changed, 36 insertions(+)
> 
> diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst index
> 820a385b06..29309abe4d 100644
> --- a/doc/guides/nics/ice.rst
> +++ b/doc/guides/nics/ice.rst
> @@ -411,6 +411,42 @@ To start ``testpmd``, and add vlan 10 to port 0:
> 
>      testpmd> rx_vlan add 10 0
> 
> +Diagnostic Utilities
> +--------------------
> +
> +Dump DDP Package
> +~~~~~~~~~~~~~~~~
> +
> +Dump the runtime packet processing pipeline configuration into a binary
> +file. This helps the support team diagnose hardware configuration
> +issues.
> +
> +Usage::
> +
> +    testpmd>ddp dump <port_id> <output_file>
> +
> +Dump Switch Configurations
> +~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Dump detail hardware configurations related to the switch pipeline
> +stage into a binary file.
> +
> +Usage::
> +
> +    testpmd>ddp dump switch <port_id> <output_file>
> +
> +Dump Tx Scheduling Tree
> +~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Dump the runtime Tx scheduling tree into a DOT file.
> +
> +Usage::
> +
> +    testpmd>txsched dump <port_id> <brief|detail> <output_file>
> +
> +In "brief" mode, all scheduling nodes in the tree are displayed.
> +In "detail" mode, each node's configuration parameters are also displayed.
> +
>  Limitations or Known issues
>  ---------------------------
> 
> --
> 2.31.1

Acked-by: Qiming Yang <qiming.yang@intel.com>

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v4 1/2] net/ice: reset Tx sched node during commit
  2023-12-26 18:54 [PATCH 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
                   ` (2 preceding siblings ...)
  2023-12-28 10:10 ` [PATCH v3 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
@ 2024-01-02 10:28 ` Qi Zhang
  2024-01-02 10:29   ` [PATCH v4 2/2] net/ice: support Tx sched commit before dev_start Qi Zhang
  2024-01-02 10:32 ` [PATCH 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 22+ messages in thread
From: Qi Zhang @ 2024-01-02 10:28 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

1. Always reset all Tx scheduler at the begining of a commit action.
   This prevent unexpected remains from previous commit.
2. Reset all Tx scheduler nodes if a commit failed.

For leaf node, stop queues which will remove sched node from
scheduler tree, then start queues which will add sched node back to
default topo.
For noleaf node, simply reset to default parameters.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
v4:
- show node type in brief mode.

v3:
- fix incorrect parameter when query rl profile

v2:
- fix CI build issue

 drivers/net/ice/ice_ethdev.h |   1 +
 drivers/net/ice/ice_tm.c     | 134 ++++++++++++++++++++++++++++-------
 2 files changed, 111 insertions(+), 24 deletions(-)

diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index 1338c80d14..3b2db6aaa6 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -478,6 +478,7 @@ struct ice_tm_node {
 	struct ice_tm_node **children;
 	struct ice_tm_shaper_profile *shaper_profile;
 	struct rte_tm_node_params params;
+	struct ice_sched_node *sched_node;
 };
 
 /* node type of Traffic Manager */
diff --git a/drivers/net/ice/ice_tm.c b/drivers/net/ice/ice_tm.c
index 1a30524b05..2ae55418b0 100644
--- a/drivers/net/ice/ice_tm.c
+++ b/drivers/net/ice/ice_tm.c
@@ -764,16 +764,94 @@ static int ice_cfg_hw_node(struct ice_hw *hw,
 	return 0;
 }
 
+static struct ice_sched_node *ice_get_vsi_node(struct ice_hw *hw)
+{
+	struct ice_sched_node *node = hw->port_info->root;
+	uint32_t vsi_layer = hw->num_tx_sched_layers - ICE_VSI_LAYER_OFFSET;
+	uint32_t i;
+
+	for (i = 0; i < vsi_layer; i++)
+		node = node->children[0];
+
+	return node;
+}
+
+static int ice_reset_noleaf_nodes(struct rte_eth_dev *dev)
+{
+	struct ice_pf *pf = ICE_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ice_tm_node_list *qgroup_list = &pf->tm_conf.qgroup_list;
+	struct ice_sched_node *vsi_node = ice_get_vsi_node(hw);
+	struct ice_tm_node *tm_node;
+	int ret;
+
+	/* reset vsi_node */
+	ret = ice_set_node_rate(hw, NULL, vsi_node->info.node_teid, ICE_AGG_TYPE_VSI);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "reset vsi node failed");
+		return ret;
+	}
+
+	/* reset queue group nodes */
+	TAILQ_FOREACH(tm_node, qgroup_list, node) {
+		if (tm_node->sched_node == NULL)
+			continue;
+
+		ret = ice_cfg_hw_node(hw, NULL,
+				      tm_node->sched_node,
+				      ICE_AGG_TYPE_Q);
+
+		if (ret) {
+			PMD_DRV_LOG(ERR, "reset queue group node %u failed", tm_node->id);
+			return ret;
+		}
+		tm_node->sched_node = NULL;
+	}
+
+	return 0;
+}
+
+static int ice_remove_leaf_nodes(struct rte_eth_dev *dev)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < dev->data->nb_tx_queues; i++) {
+		ret = ice_tx_queue_stop(dev, i);
+		if (ret) {
+			PMD_DRV_LOG(ERR, "stop queue %u failed", i);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int ice_add_leaf_nodes(struct rte_eth_dev *dev)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < dev->data->nb_tx_queues; i++) {
+		ret = ice_tx_queue_start(dev, i);
+		if (ret) {
+			PMD_DRV_LOG(ERR, "start queue %u failed", i);
+			break;
+		}
+	}
+
+	return ret;
+}
+
 static int ice_hierarchy_commit(struct rte_eth_dev *dev,
 				 int clear_on_fail,
-				 __rte_unused struct rte_tm_error *error)
+				 struct rte_tm_error *error)
 {
 	struct ice_pf *pf = ICE_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	struct ice_tm_node_list *qgroup_list = &pf->tm_conf.qgroup_list;
 	struct ice_tm_node_list *queue_list = &pf->tm_conf.queue_list;
 	struct ice_tm_node *tm_node;
-	struct ice_sched_node *node;
 	struct ice_sched_node *vsi_node = NULL;
 	struct ice_sched_node *queue_node;
 	struct ice_tx_queue *txq;
@@ -785,23 +863,25 @@ static int ice_hierarchy_commit(struct rte_eth_dev *dev,
 	uint32_t nb_qg;
 	uint32_t qid;
 	uint32_t q_teid;
-	uint32_t vsi_layer;
 
-	for (i = 0; i < dev->data->nb_tx_queues; i++) {
-		ret_val = ice_tx_queue_stop(dev, i);
-		if (ret_val) {
-			error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
-			PMD_DRV_LOG(ERR, "stop queue %u failed", i);
-			goto fail_clear;
-		}
+	/* remove leaf nodes */
+	ret_val = ice_remove_leaf_nodes(dev);
+	if (ret_val) {
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		PMD_DRV_LOG(ERR, "reset no-leaf nodes failed");
+		goto fail_clear;
 	}
 
-	node = hw->port_info->root;
-	vsi_layer = hw->num_tx_sched_layers - ICE_VSI_LAYER_OFFSET;
-	for (i = 0; i < vsi_layer; i++)
-		node = node->children[0];
-	vsi_node = node;
+	/* reset no-leaf nodes. */
+	ret_val = ice_reset_noleaf_nodes(dev);
+	if (ret_val) {
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		PMD_DRV_LOG(ERR, "reset leaf nodes failed");
+		goto add_leaf;
+	}
 
+	/* config vsi node */
+	vsi_node = ice_get_vsi_node(hw);
 	tm_node = TAILQ_FIRST(&pf->tm_conf.vsi_list);
 
 	ret_val = ice_set_node_rate(hw, tm_node,
@@ -812,9 +892,10 @@ static int ice_hierarchy_commit(struct rte_eth_dev *dev,
 		PMD_DRV_LOG(ERR,
 			    "configure vsi node %u bandwidth failed",
 			    tm_node->id);
-		goto reset_vsi;
+		goto add_leaf;
 	}
 
+	/* config queue group nodes */
 	nb_vsi_child = vsi_node->num_children;
 	nb_qg = vsi_node->children[0]->num_children;
 
@@ -833,7 +914,7 @@ static int ice_hierarchy_commit(struct rte_eth_dev *dev,
 			if (ret_val) {
 				error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
 				PMD_DRV_LOG(ERR, "start queue %u failed", qid);
-				goto reset_vsi;
+				goto reset_leaf;
 			}
 			txq = dev->data->tx_queues[qid];
 			q_teid = txq->q_teid;
@@ -841,7 +922,7 @@ static int ice_hierarchy_commit(struct rte_eth_dev *dev,
 			if (queue_node == NULL) {
 				error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
 				PMD_DRV_LOG(ERR, "get queue %u node failed", qid);
-				goto reset_vsi;
+				goto reset_leaf;
 			}
 			if (queue_node->info.parent_teid == qgroup_sched_node->info.node_teid)
 				continue;
@@ -849,19 +930,20 @@ static int ice_hierarchy_commit(struct rte_eth_dev *dev,
 			if (ret_val) {
 				error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
 				PMD_DRV_LOG(ERR, "move queue %u failed", qid);
-				goto reset_vsi;
+				goto reset_leaf;
 			}
 		}
 
 		ret_val = ice_cfg_hw_node(hw, tm_node,
 					  qgroup_sched_node,
 					  ICE_AGG_TYPE_Q);
+		tm_node->sched_node = qgroup_sched_node;
 		if (ret_val) {
 			error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
 			PMD_DRV_LOG(ERR,
 				    "configure queue group node %u failed",
 				    tm_node->id);
-			goto reset_vsi;
+			goto reset_leaf;
 		}
 
 		idx_qg++;
@@ -872,10 +954,11 @@ static int ice_hierarchy_commit(struct rte_eth_dev *dev,
 		if (idx_vsi_child >= nb_vsi_child) {
 			error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
 			PMD_DRV_LOG(ERR, "too many queues");
-			goto reset_vsi;
+			goto reset_leaf;
 		}
 	}
 
+	/* config queue nodes */
 	TAILQ_FOREACH(tm_node, queue_list, node) {
 		qid = tm_node->id;
 		txq = dev->data->tx_queues[qid];
@@ -890,14 +973,17 @@ static int ice_hierarchy_commit(struct rte_eth_dev *dev,
 			PMD_DRV_LOG(ERR,
 				    "configure queue group node %u failed",
 				    tm_node->id);
-			goto reset_vsi;
+			goto reset_leaf;
 		}
 	}
 
 	return ret_val;
 
-reset_vsi:
-	ice_set_node_rate(hw, NULL, vsi_node->info.node_teid, ICE_AGG_TYPE_VSI);
+reset_leaf:
+	ice_remove_leaf_nodes(dev);
+add_leaf:
+	ice_add_leaf_nodes(dev);
+	ice_reset_noleaf_nodes(dev);
 fail_clear:
 	/* clear all the traffic manager configuration */
 	if (clear_on_fail) {
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v4 2/2] net/ice: support Tx sched commit before dev_start
  2024-01-02 10:28 ` [PATCH v4 1/2] net/ice: reset Tx sched node during commit Qi Zhang
@ 2024-01-02 10:29   ` Qi Zhang
  0 siblings, 0 replies; 22+ messages in thread
From: Qi Zhang @ 2024-01-02 10:29 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Currently Tx hierarchy commit only take effect if device
already be started, as after a dev start / stop cycle, queues
has been removed and added back which cause the Tx scheduler
tree return to orignal topo.

In this patch, the hierarchy commit function will simply return
if device has not be started yet and all the commit actions will
be deferred to dev_start.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
 drivers/net/ice/ice_ethdev.c |  9 +++++++++
 drivers/net/ice/ice_ethdev.h |  3 +++
 drivers/net/ice/ice_tm.c     | 23 ++++++++++++++++++++---
 3 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ice/ice_ethdev.c b/drivers/net/ice/ice_ethdev.c
index 3c3bc49dc2..d425a8f98b 100644
--- a/drivers/net/ice/ice_ethdev.c
+++ b/drivers/net/ice/ice_ethdev.c
@@ -3717,6 +3717,7 @@ ice_dev_start(struct rte_eth_dev *dev)
 	int mask, ret;
 	uint8_t timer = hw->func_caps.ts_func_info.tmr_index_owned;
 	uint32_t pin_idx = ad->devargs.pin_idx;
+	struct rte_tm_error tm_err;
 
 	/* program Tx queues' context in hardware */
 	for (nb_txq = 0; nb_txq < data->nb_tx_queues; nb_txq++) {
@@ -3746,6 +3747,14 @@ ice_dev_start(struct rte_eth_dev *dev)
 		}
 	}
 
+	if (pf->tm_conf.committed) {
+		ret = ice_do_hierarchy_commit(dev, true, &tm_err);
+		if (ret) {
+			PMD_DRV_LOG(ERR, "fail to commit Tx scheduler");
+			goto rx_err;
+		}
+	}
+
 	ice_set_rx_function(dev);
 	ice_set_tx_function(dev);
 
diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index 3b2db6aaa6..5448dff48d 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -686,6 +686,9 @@ int ice_rem_rss_cfg_wrap(struct ice_pf *pf, uint16_t vsi_id,
 			 struct ice_rss_hash_cfg *cfg);
 void ice_tm_conf_init(struct rte_eth_dev *dev);
 void ice_tm_conf_uninit(struct rte_eth_dev *dev);
+int ice_do_hierarchy_commit(struct rte_eth_dev *dev,
+			    int clear_on_fail,
+			    struct rte_tm_error *error);
 extern const struct rte_tm_ops ice_tm_ops;
 
 static inline int
diff --git a/drivers/net/ice/ice_tm.c b/drivers/net/ice/ice_tm.c
index 2ae55418b0..26a440124a 100644
--- a/drivers/net/ice/ice_tm.c
+++ b/drivers/net/ice/ice_tm.c
@@ -843,9 +843,9 @@ static int ice_add_leaf_nodes(struct rte_eth_dev *dev)
 	return ret;
 }
 
-static int ice_hierarchy_commit(struct rte_eth_dev *dev,
-				 int clear_on_fail,
-				 struct rte_tm_error *error)
+int ice_do_hierarchy_commit(struct rte_eth_dev *dev,
+			    int clear_on_fail,
+			    struct rte_tm_error *error)
 {
 	struct ice_pf *pf = ICE_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
@@ -977,6 +977,8 @@ static int ice_hierarchy_commit(struct rte_eth_dev *dev,
 		}
 	}
 
+	pf->tm_conf.committed = true;
+
 	return ret_val;
 
 reset_leaf:
@@ -992,3 +994,18 @@ static int ice_hierarchy_commit(struct rte_eth_dev *dev,
 	}
 	return ret_val;
 }
+
+static int ice_hierarchy_commit(struct rte_eth_dev *dev,
+				 int clear_on_fail,
+				 struct rte_tm_error *error)
+{
+	struct ice_pf *pf = ICE_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+
+	/* if device not started, simply set committed flag and return. */
+	if (!dev->data->dev_started) {
+		pf->tm_conf.committed = true;
+		return 0;
+	}
+
+	return ice_do_hierarchy_commit(dev, clear_on_fail, error);
+}
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH 1/2] net/ice:  add Tx scheduling tree dump support
  2023-12-26 18:54 [PATCH 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
                   ` (3 preceding siblings ...)
  2024-01-02 10:28 ` [PATCH v4 1/2] net/ice: reset Tx sched node during commit Qi Zhang
@ 2024-01-02 10:32 ` Qi Zhang
  2024-01-02 10:32   ` [PATCH 2/2] doc: add document for diagnostic utilities Qi Zhang
  2024-01-02 10:33 ` [PATCH v4 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 22+ messages in thread
From: Qi Zhang @ 2024-01-02 10:32 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Added Testpmd CLI support for dumping Tx scheduling tree.

Usage:
testpmd>txsched dump <port_id> <brief|detail> <filename>

The output file is in "dot" format, which can be converted
into an image file using Graphviz.

- In "brief" mode, all scheduling nodes in the tree are displayed.
- In "detail" mode, each node's configuration parameters are also
  displayed.

Renamed `ice_ddp_package.c` to `ice_diagnose.c`, which now contains
all CLI support for diagnostic purposes.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
v4:
- show node type in brief mode.

v3:
- fix incorrect parameter when query rl profile

v2:
- fix CI build issue
 .../ice/{ice_ddp_package.c => ice_diagnose.c} | 367 ++++++++++++++++++
 drivers/net/ice/ice_ethdev.h                  |   3 +
 drivers/net/ice/ice_testpmd.c                 |  65 ++++
 drivers/net/ice/meson.build                   |   2 +-
 drivers/net/ice/version.map                   |   1 +
 5 files changed, 437 insertions(+), 1 deletion(-)
 rename drivers/net/ice/{ice_ddp_package.c => ice_diagnose.c} (61%)

diff --git a/drivers/net/ice/ice_ddp_package.c b/drivers/net/ice/ice_diagnose.c
similarity index 61%
rename from drivers/net/ice/ice_ddp_package.c
rename to drivers/net/ice/ice_diagnose.c
index 0aa19eb282..f259244825 100644
--- a/drivers/net/ice/ice_ddp_package.c
+++ b/drivers/net/ice/ice_diagnose.c
@@ -11,6 +11,7 @@
 #include <rte_tailq.h>
 
 #include "ice_ethdev.h"
+#include "ice_rxtx.h"
 
 #define ICE_BLK_MAX_COUNT          512
 #define ICE_BUFF_SEG_HEADER_FLAG   0x1
@@ -507,3 +508,369 @@ int rte_pmd_ice_dump_switch(uint16_t port, uint8_t **buff, uint32_t *size)
 
 	return ice_dump_switch(dev, buff, size);
 }
+
+static void print_rl_profile(struct ice_aqc_rl_profile_elem *prof,
+			     FILE *stream)
+{
+	fprintf(stream, "\t\t\t\t\t<td>\n");
+	fprintf(stream, "\t\t\t\t\t\t<table>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>id</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->profile_id);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>max burst size</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->max_burst_size);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>rate limit multiply</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->rl_multiply);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>wake up calculation</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->wake_up_calc);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>rate limit encode</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->rl_encode);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t</table>\n");
+	fprintf(stream, "\t\t\t\t\t</td>\n");
+}
+
+static
+void print_elem_type(FILE *stream, u8 type)
+{
+	switch (type) {
+	case 1:
+		fprintf(stream, "root");
+		break;
+	case 2:
+		fprintf(stream, "tc");
+		break;
+	case 3:
+		fprintf(stream, "se_generic");
+		break;
+	case 4:
+		fprintf(stream, "entry_point");
+		break;
+	case 5:
+		fprintf(stream, "leaf");
+		break;
+	default:
+		fprintf(stream, "%d", type);
+		break;
+	}
+}
+
+static
+void print_valid_sections(FILE *stream, u8 vs)
+{
+	if ((vs & 0x1) != 0)
+		fprintf(stream, "generic ");
+	if ((vs & 0x2) != 0)
+		fprintf(stream, "cir ");
+	if ((vs & 0x4) != 0)
+		fprintf(stream, "eir ");
+	if ((vs & 0x8) != 0)
+		fprintf(stream, "shared ");
+}
+
+static
+void print_scheduling_mode(FILE *stream, bool flag)
+{
+	if (flag)
+		fprintf(stream, "pps");
+	else
+		fprintf(stream, "bps");
+}
+
+static
+void print_priority_mode(FILE *stream, bool flag)
+{
+	if (flag)
+		fprintf(stream, "single priority node");
+	else
+		fprintf(stream, "wfq");
+}
+
+static
+void print_node(struct ice_aqc_txsched_elem_data *data,
+		struct ice_aqc_rl_profile_elem *cir_prof,
+		struct ice_aqc_rl_profile_elem *eir_prof,
+		struct ice_aqc_rl_profile_elem *shared_prof,
+		bool detail, FILE *stream)
+{
+	fprintf(stream, "\tNODE_%d [\n", data->node_teid);
+	fprintf(stream, "\t\tlabel=<\n");
+
+	fprintf(stream, "\t\t\t<table>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> teid </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->node_teid);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> type </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_elem_type(stream, data->data.elem_type);
+	fprintf(stream, "</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	if (!detail)
+		goto brief;
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> valid sections </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_valid_sections(stream, data->data.valid_sections);
+	fprintf(stream,	"</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> scheduling mode </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_scheduling_mode(stream, (data->data.generic & 0x1) != 0);
+	fprintf(stream, "</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> priority </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", (data->data.generic >> 1) & 0x7);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> priority mode</td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_priority_mode(stream, ((data->data.generic >> 4) & 0x1) != 0);
+	fprintf(stream,	"</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> adjustment value </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", (data->data.generic >> 5) & 0x3);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> suspended </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.flags & 0x1);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> cir bw profile </td>\n");
+	if (cir_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(cir_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> cir bw weight </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.cir_bw.bw_alloc);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> eir bw profile </td>\n");
+	if (eir_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(eir_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> eir bw weight </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.eir_bw.bw_alloc);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> shared rl profile </td>\n");
+	if (shared_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(shared_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+brief:
+	fprintf(stream, "\t\t\t</table>\n");
+
+	fprintf(stream, "\t\t>\n");
+	fprintf(stream, "\t\tshape=plain\n");
+	fprintf(stream, "\t]\n");
+
+	if (data->parent_teid != 0xFFFFFFFF)
+		fprintf(stream, "\tNODE_%d -> NODE_%d\n", data->parent_teid, data->node_teid);
+}
+
+static
+int query_rl_profile(struct ice_hw *hw,
+		     uint8_t level, uint8_t flags, uint16_t profile_id,
+		     struct ice_aqc_rl_profile_elem *data)
+{
+	enum ice_status ice_status;
+
+	data->level = level;
+	data->flags = flags;
+	data->profile_id = profile_id;
+
+	ice_status = ice_aq_query_rl_profile(hw, 1, data,
+					     sizeof(struct ice_aqc_rl_profile_elem), NULL);
+
+	if (ice_status != ICE_SUCCESS) {
+		PMD_DRV_LOG(ERR, "Failed to query rl profile.");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static
+int query_node(struct ice_hw *hw, uint32_t child, uint32_t *parent,
+	       uint8_t level, bool detail, FILE *stream)
+{
+	struct ice_aqc_txsched_elem_data data;
+	enum ice_status status;
+	struct ice_aqc_rl_profile_elem cir_prof;
+	struct ice_aqc_rl_profile_elem eir_prof;
+	struct ice_aqc_rl_profile_elem shared_prof;
+	struct ice_aqc_rl_profile_elem *cp = NULL;
+	struct ice_aqc_rl_profile_elem *ep = NULL;
+	struct ice_aqc_rl_profile_elem *sp = NULL;
+	int ret;
+
+	(void)level;
+	status = ice_sched_query_elem(hw, child, &data);
+	if (status != ICE_SUCCESS) {
+		PMD_DRV_LOG(ERR, "Failed to query scheduling node.");
+		return -EINVAL;
+	}
+
+	*parent = data.parent_teid;
+
+	if (data.data.cir_bw.bw_profile_idx != 0) {
+		ret = query_rl_profile(hw, level, 0, data.data.cir_bw.bw_profile_idx, &cir_prof);
+
+		if (ret)
+			return ret;
+		cp = &cir_prof;
+	}
+
+	if (data.data.eir_bw.bw_profile_idx != 0) {
+		ret = query_rl_profile(hw, level, 1, data.data.eir_bw.bw_profile_idx, &eir_prof);
+
+		if (ret)
+			return ret;
+		ep = &eir_prof;
+	}
+
+	if (data.data.srl_id != 0) {
+		ret = query_rl_profile(hw, level, 2, data.data.srl_id, &shared_prof);
+
+		if (ret)
+			return ret;
+		sp = &shared_prof;
+	}
+
+	print_node(&data, cp, ep, sp, detail, stream);
+
+	return 0;
+}
+
+static
+int query_nodes(struct ice_hw *hw,
+		uint32_t *children, int child_num,
+		uint32_t *parents, int *parent_num,
+		uint8_t level, bool detail,
+		FILE *stream)
+{
+	uint32_t parent;
+	int i;
+	int j;
+
+	*parent_num = 0;
+	for (i = 0; i < child_num; i++) {
+		bool exist = false;
+		int ret;
+
+		ret = query_node(hw, children[i], &parent, level, detail, stream);
+		if (ret)
+			return -EINVAL;
+
+		for (j = 0; j < *parent_num; j++) {
+			if (parents[j] == parent) {
+				exist = true;
+				break;
+			}
+		}
+
+		if (!exist && parent != 0xFFFFFFFF)
+			parents[(*parent_num)++] = parent;
+	}
+
+	return 0;
+}
+
+int rte_pmd_ice_dump_txsched(uint16_t port, bool detail, FILE *stream)
+{
+	struct rte_eth_dev *dev;
+	struct ice_hw *hw;
+	struct ice_pf *pf;
+	struct ice_q_ctx *q_ctx;
+	uint16_t q_num;
+	uint16_t i;
+	struct ice_tx_queue *txq;
+	uint32_t buf1[256];
+	uint32_t buf2[256];
+	uint32_t *children = buf1;
+	uint32_t *parents = buf2;
+	int child_num = 0;
+	int parent_num = 0;
+	uint8_t level;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+	if (!is_ice_supported(dev))
+		return -ENOTSUP;
+
+	dev = &rte_eth_devices[port];
+	hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	pf = ICE_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	level = hw->num_tx_sched_layers;
+
+	q_num = dev->data->nb_tx_queues;
+
+	/* main vsi */
+	for (i = 0; i < q_num; i++) {
+		txq = dev->data->tx_queues[i];
+		q_ctx = ice_get_lan_q_ctx(hw, txq->vsi->idx, 0, i);
+		children[child_num++] = q_ctx->q_teid;
+	}
+
+	/* fdir vsi */
+	q_ctx = ice_get_lan_q_ctx(hw, pf->fdir.fdir_vsi->idx, 0, 0);
+	children[child_num++] = q_ctx->q_teid;
+
+	fprintf(stream, "digraph tx_sched {\n");
+	while (child_num > 0) {
+		int ret;
+		ret = query_nodes(hw, children, child_num,
+				  parents, &parent_num,
+				  level, detail, stream);
+		if (ret)
+			return ret;
+
+		children = parents;
+		child_num = parent_num;
+		level--;
+	}
+	fprintf(stream, "}\n");
+
+	return 0;
+}
diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index d607f028e0..1338c80d14 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -739,4 +739,7 @@ int rte_pmd_ice_dump_package(uint16_t port, uint8_t **buff, uint32_t *size);
 
 __rte_experimental
 int rte_pmd_ice_dump_switch(uint16_t port, uint8_t **buff, uint32_t *size);
+
+__rte_experimental
+int rte_pmd_ice_dump_txsched(uint16_t port, bool detail, FILE *stream);
 #endif /* _ICE_ETHDEV_H_ */
diff --git a/drivers/net/ice/ice_testpmd.c b/drivers/net/ice/ice_testpmd.c
index a7a8d0c53c..98c02d68c6 100644
--- a/drivers/net/ice/ice_testpmd.c
+++ b/drivers/net/ice/ice_testpmd.c
@@ -3,6 +3,7 @@
  */
 
 #include <stdlib.h>
+#include <stdio.h>
 
 #include <cmdline_parse_num.h>
 #include <cmdline_parse_string.h>
@@ -148,6 +149,63 @@ cmdline_parse_inst_t cmd_ddp_dump_switch = {
 	},
 };
 
+/* Dump Tx Scheduling Tree configuration, only for ice PF */
+struct cmd_txsched_dump_result {
+	cmdline_fixed_string_t txsched;
+	cmdline_fixed_string_t dump;
+	portid_t port_id;
+	cmdline_fixed_string_t mode;
+	char filepath[];
+};
+
+cmdline_parse_token_string_t cmd_txsched_dump_txsched =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, txsched, "txsched");
+cmdline_parse_token_string_t cmd_txsched_dump_dump =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, dump, "dump");
+cmdline_parse_token_num_t cmd_txsched_dump_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_txsched_dump_result, port_id, RTE_UINT16);
+cmdline_parse_token_string_t cmd_txsched_dump_mode =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, mode, "brief#detail");
+cmdline_parse_token_string_t cmd_txsched_dump_filepath =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, filepath, NULL);
+
+static void
+cmd_txsched_dump_parsed(void *parsed_result,
+			__rte_unused struct cmdline *cl,
+			__rte_unused void *data)
+{
+	struct cmd_txsched_dump_result *res = parsed_result;
+	bool detail = false;
+	FILE *fp;
+
+	if (!strcmp(res->mode, "detail"))
+		detail = true;
+
+	fp = fopen(res->filepath, "w");
+	if (fp == NULL) {
+		fprintf(stderr, "Failed to open file\n");
+		return;
+	}
+
+	if (rte_pmd_ice_dump_txsched(res->port_id, detail, fp))
+		fprintf(stderr, "Failed to dump Tx scheduring runtime configure.\n");
+	fclose(fp);
+}
+
+cmdline_parse_inst_t cmd_txsched_dump = {
+	.f = cmd_txsched_dump_parsed,
+	.data = NULL,
+	.help_str = "txsched dump <port_id> <brief|detail> <config_path>",
+	.tokens = {
+		(void *)&cmd_txsched_dump_txsched,
+		(void *)&cmd_txsched_dump_dump,
+		(void *)&cmd_txsched_dump_port_id,
+		(void *)&cmd_txsched_dump_mode,
+		(void *)&cmd_txsched_dump_filepath,
+		NULL,
+	},
+};
+
 static struct testpmd_driver_commands ice_cmds = {
 	.commands = {
 	{
@@ -161,8 +219,15 @@ static struct testpmd_driver_commands ice_cmds = {
 		"ddp dump switch (port_id) (config_path)\n"
 		"    Dump a runtime switch configure on a port\n\n",
 
+	},
+	{
+		&cmd_txsched_dump,
+		"txsched dump (port_id) <brief|detail> (config_path)\n"
+		"    Dump tx scheduling runtime configure on a port\n\n",
+
 	},
 	{ NULL, NULL },
 	},
 };
+
 TESTPMD_ADD_DRIVER_COMMANDS(ice_cmds)
diff --git a/drivers/net/ice/meson.build b/drivers/net/ice/meson.build
index a957fc5d3a..b7f2188e62 100644
--- a/drivers/net/ice/meson.build
+++ b/drivers/net/ice/meson.build
@@ -6,7 +6,7 @@ objs = [base_objs]
 
 sources = files(
         'ice_acl_filter.c',
-        'ice_ddp_package.c',
+        'ice_diagnose.c',
         'ice_ethdev.c',
         'ice_fdir_filter.c',
         'ice_generic_flow.c',
diff --git a/drivers/net/ice/version.map b/drivers/net/ice/version.map
index 4e924c8f4d..24b425d6f7 100644
--- a/drivers/net/ice/version.map
+++ b/drivers/net/ice/version.map
@@ -8,4 +8,5 @@ EXPERIMENTAL {
 	# added in 19.11
 	rte_pmd_ice_dump_package;
 	rte_pmd_ice_dump_switch;
+	rte_pmd_ice_dump_txsched;
 };
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH 2/2] doc: add document for diagnostic utilities
  2024-01-02 10:32 ` [PATCH 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
@ 2024-01-02 10:32   ` Qi Zhang
  0 siblings, 0 replies; 22+ messages in thread
From: Qi Zhang @ 2024-01-02 10:32 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Document CLI for diagnose purpose.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
 doc/guides/nics/ice.rst | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
index 820a385b06..29309abe4d 100644
--- a/doc/guides/nics/ice.rst
+++ b/doc/guides/nics/ice.rst
@@ -411,6 +411,42 @@ To start ``testpmd``, and add vlan 10 to port 0:
 
     testpmd> rx_vlan add 10 0
 
+Diagnostic Utilities
+--------------------
+
+Dump DDP Package
+~~~~~~~~~~~~~~~~
+
+Dump the runtime packet processing pipeline configuration into a
+binary file. This helps the support team diagnose hardware
+configuration issues.
+
+Usage::
+
+    testpmd>ddp dump <port_id> <output_file>
+
+Dump Switch Configurations
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Dump detail hardware configurations related to the switch pipeline
+stage into a binary file.
+
+Usage::
+
+    testpmd>ddp dump switch <port_id> <output_file>
+
+Dump Tx Scheduling Tree
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Dump the runtime Tx scheduling tree into a DOT file.
+
+Usage::
+
+    testpmd>txsched dump <port_id> <brief|detail> <output_file>
+
+In "brief" mode, all scheduling nodes in the tree are displayed.
+In "detail" mode, each node's configuration parameters are also displayed.
+
 Limitations or Known issues
 ---------------------------
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v4 1/2] net/ice:  add Tx scheduling tree dump support
  2023-12-26 18:54 [PATCH 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
                   ` (4 preceding siblings ...)
  2024-01-02 10:32 ` [PATCH 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
@ 2024-01-02 10:33 ` Qi Zhang
  2024-01-02 10:33   ` [PATCH v4 2/2] doc: add document for diagnostic utilities Qi Zhang
  2024-01-02 12:24 ` [PATCH v5 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 22+ messages in thread
From: Qi Zhang @ 2024-01-02 10:33 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Added Testpmd CLI support for dumping Tx scheduling tree.

Usage:
testpmd>txsched dump <port_id> <brief|detail> <filename>

The output file is in "dot" format, which can be converted
into an image file using Graphviz.

- In "brief" mode, all scheduling nodes in the tree are displayed.
- In "detail" mode, each node's configuration parameters are also
  displayed.

Renamed `ice_ddp_package.c` to `ice_diagnose.c`, which now contains
all CLI support for diagnostic purposes.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
v4:
- show node type in brief mode

v3:
- fix incorrect parameter when query rl profile

v2:
- fix CI build issue

 .../ice/{ice_ddp_package.c => ice_diagnose.c} | 367 ++++++++++++++++++
 drivers/net/ice/ice_ethdev.h                  |   3 +
 drivers/net/ice/ice_testpmd.c                 |  65 ++++
 drivers/net/ice/meson.build                   |   2 +-
 drivers/net/ice/version.map                   |   1 +
 5 files changed, 437 insertions(+), 1 deletion(-)
 rename drivers/net/ice/{ice_ddp_package.c => ice_diagnose.c} (61%)

diff --git a/drivers/net/ice/ice_ddp_package.c b/drivers/net/ice/ice_diagnose.c
similarity index 61%
rename from drivers/net/ice/ice_ddp_package.c
rename to drivers/net/ice/ice_diagnose.c
index 0aa19eb282..f259244825 100644
--- a/drivers/net/ice/ice_ddp_package.c
+++ b/drivers/net/ice/ice_diagnose.c
@@ -11,6 +11,7 @@
 #include <rte_tailq.h>
 
 #include "ice_ethdev.h"
+#include "ice_rxtx.h"
 
 #define ICE_BLK_MAX_COUNT          512
 #define ICE_BUFF_SEG_HEADER_FLAG   0x1
@@ -507,3 +508,369 @@ int rte_pmd_ice_dump_switch(uint16_t port, uint8_t **buff, uint32_t *size)
 
 	return ice_dump_switch(dev, buff, size);
 }
+
+static void print_rl_profile(struct ice_aqc_rl_profile_elem *prof,
+			     FILE *stream)
+{
+	fprintf(stream, "\t\t\t\t\t<td>\n");
+	fprintf(stream, "\t\t\t\t\t\t<table>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>id</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->profile_id);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>max burst size</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->max_burst_size);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>rate limit multiply</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->rl_multiply);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>wake up calculation</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->wake_up_calc);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>rate limit encode</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->rl_encode);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t</table>\n");
+	fprintf(stream, "\t\t\t\t\t</td>\n");
+}
+
+static
+void print_elem_type(FILE *stream, u8 type)
+{
+	switch (type) {
+	case 1:
+		fprintf(stream, "root");
+		break;
+	case 2:
+		fprintf(stream, "tc");
+		break;
+	case 3:
+		fprintf(stream, "se_generic");
+		break;
+	case 4:
+		fprintf(stream, "entry_point");
+		break;
+	case 5:
+		fprintf(stream, "leaf");
+		break;
+	default:
+		fprintf(stream, "%d", type);
+		break;
+	}
+}
+
+static
+void print_valid_sections(FILE *stream, u8 vs)
+{
+	if ((vs & 0x1) != 0)
+		fprintf(stream, "generic ");
+	if ((vs & 0x2) != 0)
+		fprintf(stream, "cir ");
+	if ((vs & 0x4) != 0)
+		fprintf(stream, "eir ");
+	if ((vs & 0x8) != 0)
+		fprintf(stream, "shared ");
+}
+
+static
+void print_scheduling_mode(FILE *stream, bool flag)
+{
+	if (flag)
+		fprintf(stream, "pps");
+	else
+		fprintf(stream, "bps");
+}
+
+static
+void print_priority_mode(FILE *stream, bool flag)
+{
+	if (flag)
+		fprintf(stream, "single priority node");
+	else
+		fprintf(stream, "wfq");
+}
+
+static
+void print_node(struct ice_aqc_txsched_elem_data *data,
+		struct ice_aqc_rl_profile_elem *cir_prof,
+		struct ice_aqc_rl_profile_elem *eir_prof,
+		struct ice_aqc_rl_profile_elem *shared_prof,
+		bool detail, FILE *stream)
+{
+	fprintf(stream, "\tNODE_%d [\n", data->node_teid);
+	fprintf(stream, "\t\tlabel=<\n");
+
+	fprintf(stream, "\t\t\t<table>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> teid </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->node_teid);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> type </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_elem_type(stream, data->data.elem_type);
+	fprintf(stream, "</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	if (!detail)
+		goto brief;
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> valid sections </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_valid_sections(stream, data->data.valid_sections);
+	fprintf(stream,	"</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> scheduling mode </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_scheduling_mode(stream, (data->data.generic & 0x1) != 0);
+	fprintf(stream, "</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> priority </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", (data->data.generic >> 1) & 0x7);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> priority mode</td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_priority_mode(stream, ((data->data.generic >> 4) & 0x1) != 0);
+	fprintf(stream,	"</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> adjustment value </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", (data->data.generic >> 5) & 0x3);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> suspended </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.flags & 0x1);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> cir bw profile </td>\n");
+	if (cir_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(cir_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> cir bw weight </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.cir_bw.bw_alloc);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> eir bw profile </td>\n");
+	if (eir_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(eir_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> eir bw weight </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.eir_bw.bw_alloc);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> shared rl profile </td>\n");
+	if (shared_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(shared_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+brief:
+	fprintf(stream, "\t\t\t</table>\n");
+
+	fprintf(stream, "\t\t>\n");
+	fprintf(stream, "\t\tshape=plain\n");
+	fprintf(stream, "\t]\n");
+
+	if (data->parent_teid != 0xFFFFFFFF)
+		fprintf(stream, "\tNODE_%d -> NODE_%d\n", data->parent_teid, data->node_teid);
+}
+
+static
+int query_rl_profile(struct ice_hw *hw,
+		     uint8_t level, uint8_t flags, uint16_t profile_id,
+		     struct ice_aqc_rl_profile_elem *data)
+{
+	enum ice_status ice_status;
+
+	data->level = level;
+	data->flags = flags;
+	data->profile_id = profile_id;
+
+	ice_status = ice_aq_query_rl_profile(hw, 1, data,
+					     sizeof(struct ice_aqc_rl_profile_elem), NULL);
+
+	if (ice_status != ICE_SUCCESS) {
+		PMD_DRV_LOG(ERR, "Failed to query rl profile.");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static
+int query_node(struct ice_hw *hw, uint32_t child, uint32_t *parent,
+	       uint8_t level, bool detail, FILE *stream)
+{
+	struct ice_aqc_txsched_elem_data data;
+	enum ice_status status;
+	struct ice_aqc_rl_profile_elem cir_prof;
+	struct ice_aqc_rl_profile_elem eir_prof;
+	struct ice_aqc_rl_profile_elem shared_prof;
+	struct ice_aqc_rl_profile_elem *cp = NULL;
+	struct ice_aqc_rl_profile_elem *ep = NULL;
+	struct ice_aqc_rl_profile_elem *sp = NULL;
+	int ret;
+
+	(void)level;
+	status = ice_sched_query_elem(hw, child, &data);
+	if (status != ICE_SUCCESS) {
+		PMD_DRV_LOG(ERR, "Failed to query scheduling node.");
+		return -EINVAL;
+	}
+
+	*parent = data.parent_teid;
+
+	if (data.data.cir_bw.bw_profile_idx != 0) {
+		ret = query_rl_profile(hw, level, 0, data.data.cir_bw.bw_profile_idx, &cir_prof);
+
+		if (ret)
+			return ret;
+		cp = &cir_prof;
+	}
+
+	if (data.data.eir_bw.bw_profile_idx != 0) {
+		ret = query_rl_profile(hw, level, 1, data.data.eir_bw.bw_profile_idx, &eir_prof);
+
+		if (ret)
+			return ret;
+		ep = &eir_prof;
+	}
+
+	if (data.data.srl_id != 0) {
+		ret = query_rl_profile(hw, level, 2, data.data.srl_id, &shared_prof);
+
+		if (ret)
+			return ret;
+		sp = &shared_prof;
+	}
+
+	print_node(&data, cp, ep, sp, detail, stream);
+
+	return 0;
+}
+
+static
+int query_nodes(struct ice_hw *hw,
+		uint32_t *children, int child_num,
+		uint32_t *parents, int *parent_num,
+		uint8_t level, bool detail,
+		FILE *stream)
+{
+	uint32_t parent;
+	int i;
+	int j;
+
+	*parent_num = 0;
+	for (i = 0; i < child_num; i++) {
+		bool exist = false;
+		int ret;
+
+		ret = query_node(hw, children[i], &parent, level, detail, stream);
+		if (ret)
+			return -EINVAL;
+
+		for (j = 0; j < *parent_num; j++) {
+			if (parents[j] == parent) {
+				exist = true;
+				break;
+			}
+		}
+
+		if (!exist && parent != 0xFFFFFFFF)
+			parents[(*parent_num)++] = parent;
+	}
+
+	return 0;
+}
+
+int rte_pmd_ice_dump_txsched(uint16_t port, bool detail, FILE *stream)
+{
+	struct rte_eth_dev *dev;
+	struct ice_hw *hw;
+	struct ice_pf *pf;
+	struct ice_q_ctx *q_ctx;
+	uint16_t q_num;
+	uint16_t i;
+	struct ice_tx_queue *txq;
+	uint32_t buf1[256];
+	uint32_t buf2[256];
+	uint32_t *children = buf1;
+	uint32_t *parents = buf2;
+	int child_num = 0;
+	int parent_num = 0;
+	uint8_t level;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+	if (!is_ice_supported(dev))
+		return -ENOTSUP;
+
+	dev = &rte_eth_devices[port];
+	hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	pf = ICE_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	level = hw->num_tx_sched_layers;
+
+	q_num = dev->data->nb_tx_queues;
+
+	/* main vsi */
+	for (i = 0; i < q_num; i++) {
+		txq = dev->data->tx_queues[i];
+		q_ctx = ice_get_lan_q_ctx(hw, txq->vsi->idx, 0, i);
+		children[child_num++] = q_ctx->q_teid;
+	}
+
+	/* fdir vsi */
+	q_ctx = ice_get_lan_q_ctx(hw, pf->fdir.fdir_vsi->idx, 0, 0);
+	children[child_num++] = q_ctx->q_teid;
+
+	fprintf(stream, "digraph tx_sched {\n");
+	while (child_num > 0) {
+		int ret;
+		ret = query_nodes(hw, children, child_num,
+				  parents, &parent_num,
+				  level, detail, stream);
+		if (ret)
+			return ret;
+
+		children = parents;
+		child_num = parent_num;
+		level--;
+	}
+	fprintf(stream, "}\n");
+
+	return 0;
+}
diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index d607f028e0..1338c80d14 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -739,4 +739,7 @@ int rte_pmd_ice_dump_package(uint16_t port, uint8_t **buff, uint32_t *size);
 
 __rte_experimental
 int rte_pmd_ice_dump_switch(uint16_t port, uint8_t **buff, uint32_t *size);
+
+__rte_experimental
+int rte_pmd_ice_dump_txsched(uint16_t port, bool detail, FILE *stream);
 #endif /* _ICE_ETHDEV_H_ */
diff --git a/drivers/net/ice/ice_testpmd.c b/drivers/net/ice/ice_testpmd.c
index a7a8d0c53c..98c02d68c6 100644
--- a/drivers/net/ice/ice_testpmd.c
+++ b/drivers/net/ice/ice_testpmd.c
@@ -3,6 +3,7 @@
  */
 
 #include <stdlib.h>
+#include <stdio.h>
 
 #include <cmdline_parse_num.h>
 #include <cmdline_parse_string.h>
@@ -148,6 +149,63 @@ cmdline_parse_inst_t cmd_ddp_dump_switch = {
 	},
 };
 
+/* Dump Tx Scheduling Tree configuration, only for ice PF */
+struct cmd_txsched_dump_result {
+	cmdline_fixed_string_t txsched;
+	cmdline_fixed_string_t dump;
+	portid_t port_id;
+	cmdline_fixed_string_t mode;
+	char filepath[];
+};
+
+cmdline_parse_token_string_t cmd_txsched_dump_txsched =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, txsched, "txsched");
+cmdline_parse_token_string_t cmd_txsched_dump_dump =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, dump, "dump");
+cmdline_parse_token_num_t cmd_txsched_dump_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_txsched_dump_result, port_id, RTE_UINT16);
+cmdline_parse_token_string_t cmd_txsched_dump_mode =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, mode, "brief#detail");
+cmdline_parse_token_string_t cmd_txsched_dump_filepath =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, filepath, NULL);
+
+static void
+cmd_txsched_dump_parsed(void *parsed_result,
+			__rte_unused struct cmdline *cl,
+			__rte_unused void *data)
+{
+	struct cmd_txsched_dump_result *res = parsed_result;
+	bool detail = false;
+	FILE *fp;
+
+	if (!strcmp(res->mode, "detail"))
+		detail = true;
+
+	fp = fopen(res->filepath, "w");
+	if (fp == NULL) {
+		fprintf(stderr, "Failed to open file\n");
+		return;
+	}
+
+	if (rte_pmd_ice_dump_txsched(res->port_id, detail, fp))
+		fprintf(stderr, "Failed to dump Tx scheduring runtime configure.\n");
+	fclose(fp);
+}
+
+cmdline_parse_inst_t cmd_txsched_dump = {
+	.f = cmd_txsched_dump_parsed,
+	.data = NULL,
+	.help_str = "txsched dump <port_id> <brief|detail> <config_path>",
+	.tokens = {
+		(void *)&cmd_txsched_dump_txsched,
+		(void *)&cmd_txsched_dump_dump,
+		(void *)&cmd_txsched_dump_port_id,
+		(void *)&cmd_txsched_dump_mode,
+		(void *)&cmd_txsched_dump_filepath,
+		NULL,
+	},
+};
+
 static struct testpmd_driver_commands ice_cmds = {
 	.commands = {
 	{
@@ -161,8 +219,15 @@ static struct testpmd_driver_commands ice_cmds = {
 		"ddp dump switch (port_id) (config_path)\n"
 		"    Dump a runtime switch configure on a port\n\n",
 
+	},
+	{
+		&cmd_txsched_dump,
+		"txsched dump (port_id) <brief|detail> (config_path)\n"
+		"    Dump tx scheduling runtime configure on a port\n\n",
+
 	},
 	{ NULL, NULL },
 	},
 };
+
 TESTPMD_ADD_DRIVER_COMMANDS(ice_cmds)
diff --git a/drivers/net/ice/meson.build b/drivers/net/ice/meson.build
index a957fc5d3a..b7f2188e62 100644
--- a/drivers/net/ice/meson.build
+++ b/drivers/net/ice/meson.build
@@ -6,7 +6,7 @@ objs = [base_objs]
 
 sources = files(
         'ice_acl_filter.c',
-        'ice_ddp_package.c',
+        'ice_diagnose.c',
         'ice_ethdev.c',
         'ice_fdir_filter.c',
         'ice_generic_flow.c',
diff --git a/drivers/net/ice/version.map b/drivers/net/ice/version.map
index 4e924c8f4d..24b425d6f7 100644
--- a/drivers/net/ice/version.map
+++ b/drivers/net/ice/version.map
@@ -8,4 +8,5 @@ EXPERIMENTAL {
 	# added in 19.11
 	rte_pmd_ice_dump_package;
 	rte_pmd_ice_dump_switch;
+	rte_pmd_ice_dump_txsched;
 };
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v4 2/2] doc: add document for diagnostic utilities
  2024-01-02 10:33 ` [PATCH v4 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
@ 2024-01-02 10:33   ` Qi Zhang
  0 siblings, 0 replies; 22+ messages in thread
From: Qi Zhang @ 2024-01-02 10:33 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Document CLI for diagnose purpose.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
 doc/guides/nics/ice.rst | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
index 820a385b06..29309abe4d 100644
--- a/doc/guides/nics/ice.rst
+++ b/doc/guides/nics/ice.rst
@@ -411,6 +411,42 @@ To start ``testpmd``, and add vlan 10 to port 0:
 
     testpmd> rx_vlan add 10 0
 
+Diagnostic Utilities
+--------------------
+
+Dump DDP Package
+~~~~~~~~~~~~~~~~
+
+Dump the runtime packet processing pipeline configuration into a
+binary file. This helps the support team diagnose hardware
+configuration issues.
+
+Usage::
+
+    testpmd>ddp dump <port_id> <output_file>
+
+Dump Switch Configurations
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Dump detail hardware configurations related to the switch pipeline
+stage into a binary file.
+
+Usage::
+
+    testpmd>ddp dump switch <port_id> <output_file>
+
+Dump Tx Scheduling Tree
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Dump the runtime Tx scheduling tree into a DOT file.
+
+Usage::
+
+    testpmd>txsched dump <port_id> <brief|detail> <output_file>
+
+In "brief" mode, all scheduling nodes in the tree are displayed.
+In "detail" mode, each node's configuration parameters are also displayed.
+
 Limitations or Known issues
 ---------------------------
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v5 1/2] net/ice:  add Tx scheduling tree dump support
  2023-12-26 18:54 [PATCH 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
                   ` (5 preceding siblings ...)
  2024-01-02 10:33 ` [PATCH v4 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
@ 2024-01-02 12:24 ` Qi Zhang
  2024-01-02 12:24   ` [PATCH v5 2/2] doc: add document for diagnostic utilities Qi Zhang
  2024-01-02 23:30   ` [PATCH v5 1/2] net/ice: add Tx scheduling tree dump support Stephen Hemminger
  2024-01-02 12:29 ` [PATCH v6 " Qi Zhang
  2024-01-02 12:39 ` [PATCH v7 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
  8 siblings, 2 replies; 22+ messages in thread
From: Qi Zhang @ 2024-01-02 12:24 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Added Testpmd CLI support for dumping Tx scheduling tree.

Usage:
testpmd>txsched dump <port_id> <brief|detail> <filename>

The output file is in "dot" format, which can be converted
into an image file using Graphviz.

- In "brief" mode, all scheduling nodes in the tree are displayed.
- In "detail" mode, each node's configuration parameters are also
  displayed.

Renamed `ice_ddp_package.c` to `ice_diagnose.c`, which now contains
all CLI support for diagnostic purposes.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
v5:
- ignore the error when query node failed at queue level, as queue may
   be stopped.
v4:
- show node type in brief mode

v3:
- fix incorrect parameter when query rl profile

v2:
- fix CI build issue

 .../ice/{ice_ddp_package.c => ice_diagnose.c} | 373 ++++++++++++++++++
 drivers/net/ice/ice_ethdev.h                  |   3 +
 drivers/net/ice/ice_testpmd.c                 |  65 +++
 drivers/net/ice/meson.build                   |   2 +-
 drivers/net/ice/version.map                   |   1 +
 5 files changed, 443 insertions(+), 1 deletion(-)
 rename drivers/net/ice/{ice_ddp_package.c => ice_diagnose.c} (60%)

diff --git a/drivers/net/ice/ice_ddp_package.c b/drivers/net/ice/ice_diagnose.c
similarity index 60%
rename from drivers/net/ice/ice_ddp_package.c
rename to drivers/net/ice/ice_diagnose.c
index 0aa19eb282..2b9794d212 100644
--- a/drivers/net/ice/ice_ddp_package.c
+++ b/drivers/net/ice/ice_diagnose.c
@@ -11,6 +11,7 @@
 #include <rte_tailq.h>
 
 #include "ice_ethdev.h"
+#include "ice_rxtx.h"
 
 #define ICE_BLK_MAX_COUNT          512
 #define ICE_BUFF_SEG_HEADER_FLAG   0x1
@@ -507,3 +508,375 @@ int rte_pmd_ice_dump_switch(uint16_t port, uint8_t **buff, uint32_t *size)
 
 	return ice_dump_switch(dev, buff, size);
 }
+
+static void print_rl_profile(struct ice_aqc_rl_profile_elem *prof,
+			     FILE *stream)
+{
+	fprintf(stream, "\t\t\t\t\t<td>\n");
+	fprintf(stream, "\t\t\t\t\t\t<table>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>id</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->profile_id);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>max burst size</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->max_burst_size);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>rate limit multiply</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->rl_multiply);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>wake up calculation</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->wake_up_calc);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>rate limit encode</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->rl_encode);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t</table>\n");
+	fprintf(stream, "\t\t\t\t\t</td>\n");
+}
+
+static
+void print_elem_type(FILE *stream, u8 type)
+{
+	switch (type) {
+	case 1:
+		fprintf(stream, "root");
+		break;
+	case 2:
+		fprintf(stream, "tc");
+		break;
+	case 3:
+		fprintf(stream, "se_generic");
+		break;
+	case 4:
+		fprintf(stream, "entry_point");
+		break;
+	case 5:
+		fprintf(stream, "leaf");
+		break;
+	default:
+		fprintf(stream, "%d", type);
+		break;
+	}
+}
+
+static
+void print_valid_sections(FILE *stream, u8 vs)
+{
+	if ((vs & 0x1) != 0)
+		fprintf(stream, "generic ");
+	if ((vs & 0x2) != 0)
+		fprintf(stream, "cir ");
+	if ((vs & 0x4) != 0)
+		fprintf(stream, "eir ");
+	if ((vs & 0x8) != 0)
+		fprintf(stream, "shared ");
+}
+
+static
+void print_scheduling_mode(FILE *stream, bool flag)
+{
+	if (flag)
+		fprintf(stream, "pps");
+	else
+		fprintf(stream, "bps");
+}
+
+static
+void print_priority_mode(FILE *stream, bool flag)
+{
+	if (flag)
+		fprintf(stream, "single priority node");
+	else
+		fprintf(stream, "wfq");
+}
+
+static
+void print_node(struct ice_aqc_txsched_elem_data *data,
+		struct ice_aqc_rl_profile_elem *cir_prof,
+		struct ice_aqc_rl_profile_elem *eir_prof,
+		struct ice_aqc_rl_profile_elem *shared_prof,
+		bool detail, FILE *stream)
+{
+	fprintf(stream, "\tNODE_%d [\n", data->node_teid);
+	fprintf(stream, "\t\tlabel=<\n");
+
+	fprintf(stream, "\t\t\t<table>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> teid </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->node_teid);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> type </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_elem_type(stream, data->data.elem_type);
+	fprintf(stream, "</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	if (!detail)
+		goto brief;
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> valid sections </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_valid_sections(stream, data->data.valid_sections);
+	fprintf(stream,	"</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> scheduling mode </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_scheduling_mode(stream, (data->data.generic & 0x1) != 0);
+	fprintf(stream, "</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> priority </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", (data->data.generic >> 1) & 0x7);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> priority mode</td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_priority_mode(stream, ((data->data.generic >> 4) & 0x1) != 0);
+	fprintf(stream,	"</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> adjustment value </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", (data->data.generic >> 5) & 0x3);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> suspended </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.flags & 0x1);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> cir bw profile </td>\n");
+	if (cir_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(cir_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> cir bw weight </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.cir_bw.bw_alloc);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> eir bw profile </td>\n");
+	if (eir_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(eir_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> eir bw weight </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.eir_bw.bw_alloc);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> shared rl profile </td>\n");
+	if (shared_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(shared_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+brief:
+	fprintf(stream, "\t\t\t</table>\n");
+
+	fprintf(stream, "\t\t>\n");
+	fprintf(stream, "\t\tshape=plain\n");
+	fprintf(stream, "\t]\n");
+
+	if (data->parent_teid != 0xFFFFFFFF)
+		fprintf(stream, "\tNODE_%d -> NODE_%d\n", data->parent_teid, data->node_teid);
+}
+
+static
+int query_rl_profile(struct ice_hw *hw,
+		     uint8_t level, uint8_t flags, uint16_t profile_id,
+		     struct ice_aqc_rl_profile_elem *data)
+{
+	enum ice_status ice_status;
+
+	data->level = level;
+	data->flags = flags;
+	data->profile_id = profile_id;
+
+	ice_status = ice_aq_query_rl_profile(hw, 1, data,
+					     sizeof(struct ice_aqc_rl_profile_elem), NULL);
+
+	if (ice_status != ICE_SUCCESS) {
+		PMD_DRV_LOG(ERR, "Failed to query rl profile.");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static
+int query_node(struct ice_hw *hw, uint32_t child, uint32_t *parent,
+	       uint8_t level, bool detail, FILE *stream)
+{
+	struct ice_aqc_txsched_elem_data data;
+	enum ice_status status;
+	struct ice_aqc_rl_profile_elem cir_prof;
+	struct ice_aqc_rl_profile_elem eir_prof;
+	struct ice_aqc_rl_profile_elem shared_prof;
+	struct ice_aqc_rl_profile_elem *cp = NULL;
+	struct ice_aqc_rl_profile_elem *ep = NULL;
+	struct ice_aqc_rl_profile_elem *sp = NULL;
+	int ret;
+
+	status = ice_sched_query_elem(hw, child, &data);
+	if (status != ICE_SUCCESS) {
+		if (level == hw->num_tx_sched_layers) {
+			/* ignore the error when a queue has been stopped. */
+			PMD_DRV_LOG(WARNING, "Failed to query queue node %d.", child);
+			*parent = 0xffffffff;
+			return 0;
+		} else {
+			PMD_DRV_LOG(ERR, "Failed to query scheduling node %d.", child);
+			return -EINVAL;
+		}
+	}
+
+	*parent = data.parent_teid;
+
+	if (data.data.cir_bw.bw_profile_idx != 0) {
+		ret = query_rl_profile(hw, level, 0, data.data.cir_bw.bw_profile_idx, &cir_prof);
+
+		if (ret)
+			return ret;
+		cp = &cir_prof;
+	}
+
+	if (data.data.eir_bw.bw_profile_idx != 0) {
+		ret = query_rl_profile(hw, level, 1, data.data.eir_bw.bw_profile_idx, &eir_prof);
+
+		if (ret)
+			return ret;
+		ep = &eir_prof;
+	}
+
+	if (data.data.srl_id != 0) {
+		ret = query_rl_profile(hw, level, 2, data.data.srl_id, &shared_prof);
+
+		if (ret)
+			return ret;
+		sp = &shared_prof;
+	}
+
+	print_node(&data, cp, ep, sp, detail, stream);
+
+	return 0;
+}
+
+static
+int query_nodes(struct ice_hw *hw,
+		uint32_t *children, int child_num,
+		uint32_t *parents, int *parent_num,
+		uint8_t level, bool detail,
+		FILE *stream)
+{
+	uint32_t parent;
+	int i;
+	int j;
+
+	*parent_num = 0;
+	for (i = 0; i < child_num; i++) {
+		bool exist = false;
+		int ret;
+
+		ret = query_node(hw, children[i], &parent, level, detail, stream);
+		if (ret)
+			return -EINVAL;
+
+		for (j = 0; j < *parent_num; j++) {
+			if (parents[j] == parent) {
+				exist = true;
+				break;
+			}
+		}
+
+		if (!exist && parent != 0xFFFFFFFF)
+			parents[(*parent_num)++] = parent;
+	}
+
+	return 0;
+}
+
+int rte_pmd_ice_dump_txsched(uint16_t port, bool detail, FILE *stream)
+{
+	struct rte_eth_dev *dev;
+	struct ice_hw *hw;
+	struct ice_pf *pf;
+	struct ice_q_ctx *q_ctx;
+	uint16_t q_num;
+	uint16_t i;
+	struct ice_tx_queue *txq;
+	uint32_t buf1[256];
+	uint32_t buf2[256];
+	uint32_t *children = buf1;
+	uint32_t *parents = buf2;
+	int child_num = 0;
+	int parent_num = 0;
+	uint8_t level;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+	if (!is_ice_supported(dev))
+		return -ENOTSUP;
+
+	dev = &rte_eth_devices[port];
+	hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	pf = ICE_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	level = hw->num_tx_sched_layers;
+
+	q_num = dev->data->nb_tx_queues;
+
+	/* main vsi */
+	for (i = 0; i < q_num; i++) {
+		txq = dev->data->tx_queues[i];
+		q_ctx = ice_get_lan_q_ctx(hw, txq->vsi->idx, 0, i);
+		children[child_num++] = q_ctx->q_teid;
+	}
+
+	/* fdir vsi */
+	q_ctx = ice_get_lan_q_ctx(hw, pf->fdir.fdir_vsi->idx, 0, 0);
+	children[child_num++] = q_ctx->q_teid;
+
+	fprintf(stream, "digraph tx_sched {\n");
+	while (child_num > 0) {
+		int ret;
+		ret = query_nodes(hw, children, child_num,
+				  parents, &parent_num,
+				  level, detail, stream);
+		if (ret)
+			return ret;
+
+		children = parents;
+		child_num = parent_num;
+		level--;
+	}
+	fprintf(stream, "}\n");
+
+	return 0;
+}
diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index d607f028e0..1338c80d14 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -739,4 +739,7 @@ int rte_pmd_ice_dump_package(uint16_t port, uint8_t **buff, uint32_t *size);
 
 __rte_experimental
 int rte_pmd_ice_dump_switch(uint16_t port, uint8_t **buff, uint32_t *size);
+
+__rte_experimental
+int rte_pmd_ice_dump_txsched(uint16_t port, bool detail, FILE *stream);
 #endif /* _ICE_ETHDEV_H_ */
diff --git a/drivers/net/ice/ice_testpmd.c b/drivers/net/ice/ice_testpmd.c
index a7a8d0c53c..98c02d68c6 100644
--- a/drivers/net/ice/ice_testpmd.c
+++ b/drivers/net/ice/ice_testpmd.c
@@ -3,6 +3,7 @@
  */
 
 #include <stdlib.h>
+#include <stdio.h>
 
 #include <cmdline_parse_num.h>
 #include <cmdline_parse_string.h>
@@ -148,6 +149,63 @@ cmdline_parse_inst_t cmd_ddp_dump_switch = {
 	},
 };
 
+/* Dump Tx Scheduling Tree configuration, only for ice PF */
+struct cmd_txsched_dump_result {
+	cmdline_fixed_string_t txsched;
+	cmdline_fixed_string_t dump;
+	portid_t port_id;
+	cmdline_fixed_string_t mode;
+	char filepath[];
+};
+
+cmdline_parse_token_string_t cmd_txsched_dump_txsched =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, txsched, "txsched");
+cmdline_parse_token_string_t cmd_txsched_dump_dump =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, dump, "dump");
+cmdline_parse_token_num_t cmd_txsched_dump_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_txsched_dump_result, port_id, RTE_UINT16);
+cmdline_parse_token_string_t cmd_txsched_dump_mode =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, mode, "brief#detail");
+cmdline_parse_token_string_t cmd_txsched_dump_filepath =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, filepath, NULL);
+
+static void
+cmd_txsched_dump_parsed(void *parsed_result,
+			__rte_unused struct cmdline *cl,
+			__rte_unused void *data)
+{
+	struct cmd_txsched_dump_result *res = parsed_result;
+	bool detail = false;
+	FILE *fp;
+
+	if (!strcmp(res->mode, "detail"))
+		detail = true;
+
+	fp = fopen(res->filepath, "w");
+	if (fp == NULL) {
+		fprintf(stderr, "Failed to open file\n");
+		return;
+	}
+
+	if (rte_pmd_ice_dump_txsched(res->port_id, detail, fp))
+		fprintf(stderr, "Failed to dump Tx scheduring runtime configure.\n");
+	fclose(fp);
+}
+
+cmdline_parse_inst_t cmd_txsched_dump = {
+	.f = cmd_txsched_dump_parsed,
+	.data = NULL,
+	.help_str = "txsched dump <port_id> <brief|detail> <config_path>",
+	.tokens = {
+		(void *)&cmd_txsched_dump_txsched,
+		(void *)&cmd_txsched_dump_dump,
+		(void *)&cmd_txsched_dump_port_id,
+		(void *)&cmd_txsched_dump_mode,
+		(void *)&cmd_txsched_dump_filepath,
+		NULL,
+	},
+};
+
 static struct testpmd_driver_commands ice_cmds = {
 	.commands = {
 	{
@@ -161,8 +219,15 @@ static struct testpmd_driver_commands ice_cmds = {
 		"ddp dump switch (port_id) (config_path)\n"
 		"    Dump a runtime switch configure on a port\n\n",
 
+	},
+	{
+		&cmd_txsched_dump,
+		"txsched dump (port_id) <brief|detail> (config_path)\n"
+		"    Dump tx scheduling runtime configure on a port\n\n",
+
 	},
 	{ NULL, NULL },
 	},
 };
+
 TESTPMD_ADD_DRIVER_COMMANDS(ice_cmds)
diff --git a/drivers/net/ice/meson.build b/drivers/net/ice/meson.build
index a957fc5d3a..b7f2188e62 100644
--- a/drivers/net/ice/meson.build
+++ b/drivers/net/ice/meson.build
@@ -6,7 +6,7 @@ objs = [base_objs]
 
 sources = files(
         'ice_acl_filter.c',
-        'ice_ddp_package.c',
+        'ice_diagnose.c',
         'ice_ethdev.c',
         'ice_fdir_filter.c',
         'ice_generic_flow.c',
diff --git a/drivers/net/ice/version.map b/drivers/net/ice/version.map
index 4e924c8f4d..24b425d6f7 100644
--- a/drivers/net/ice/version.map
+++ b/drivers/net/ice/version.map
@@ -8,4 +8,5 @@ EXPERIMENTAL {
 	# added in 19.11
 	rte_pmd_ice_dump_package;
 	rte_pmd_ice_dump_switch;
+	rte_pmd_ice_dump_txsched;
 };
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v5 2/2] doc: add document for diagnostic utilities
  2024-01-02 12:24 ` [PATCH v5 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
@ 2024-01-02 12:24   ` Qi Zhang
  2024-01-02 23:30   ` [PATCH v5 1/2] net/ice: add Tx scheduling tree dump support Stephen Hemminger
  1 sibling, 0 replies; 22+ messages in thread
From: Qi Zhang @ 2024-01-02 12:24 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Document CLI for diagnose purpose.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
 doc/guides/nics/ice.rst | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
index 820a385b06..29309abe4d 100644
--- a/doc/guides/nics/ice.rst
+++ b/doc/guides/nics/ice.rst
@@ -411,6 +411,42 @@ To start ``testpmd``, and add vlan 10 to port 0:
 
     testpmd> rx_vlan add 10 0
 
+Diagnostic Utilities
+--------------------
+
+Dump DDP Package
+~~~~~~~~~~~~~~~~
+
+Dump the runtime packet processing pipeline configuration into a
+binary file. This helps the support team diagnose hardware
+configuration issues.
+
+Usage::
+
+    testpmd>ddp dump <port_id> <output_file>
+
+Dump Switch Configurations
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Dump detail hardware configurations related to the switch pipeline
+stage into a binary file.
+
+Usage::
+
+    testpmd>ddp dump switch <port_id> <output_file>
+
+Dump Tx Scheduling Tree
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Dump the runtime Tx scheduling tree into a DOT file.
+
+Usage::
+
+    testpmd>txsched dump <port_id> <brief|detail> <output_file>
+
+In "brief" mode, all scheduling nodes in the tree are displayed.
+In "detail" mode, each node's configuration parameters are also displayed.
+
 Limitations or Known issues
 ---------------------------
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v6 1/2] net/ice:  add Tx scheduling tree dump support
  2023-12-26 18:54 [PATCH 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
                   ` (6 preceding siblings ...)
  2024-01-02 12:24 ` [PATCH v5 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
@ 2024-01-02 12:29 ` Qi Zhang
  2024-01-02 12:29   ` [PATCH v6 2/2] doc: add document for ice diagnostic utilities Qi Zhang
  2024-01-02 12:39 ` [PATCH v7 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
  8 siblings, 1 reply; 22+ messages in thread
From: Qi Zhang @ 2024-01-02 12:29 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Added Testpmd CLI support for dumping Tx scheduling tree.

Usage:
testpmd>txsched dump <port_id> <brief|detail> <filename>

The output file is in "dot" format, which can be converted
into an image file using Graphviz.

- In "brief" mode, all scheduling nodes in the tree are displayed.
- In "detail" mode, each node's configuration parameters are also
  displayed.

Renamed `ice_ddp_package.c` to `ice_diagnose.c`, which now contains
all CLI support for diagnostic purposes.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
v5:
- ignore the error when query node failed at queue level, as queue may
   be stopped.
v4:
- show node type in brief mode

v3:
- fix incorrect parameter when query rl profile

v2:
- fix CI build issue

 .../ice/{ice_ddp_package.c => ice_diagnose.c} | 373 ++++++++++++++++++
 drivers/net/ice/ice_ethdev.h                  |   3 +
 drivers/net/ice/ice_testpmd.c                 |  65 +++
 drivers/net/ice/meson.build                   |   2 +-
 drivers/net/ice/version.map                   |   1 +
 5 files changed, 443 insertions(+), 1 deletion(-)
 rename drivers/net/ice/{ice_ddp_package.c => ice_diagnose.c} (60%)

diff --git a/drivers/net/ice/ice_ddp_package.c b/drivers/net/ice/ice_diagnose.c
similarity index 60%
rename from drivers/net/ice/ice_ddp_package.c
rename to drivers/net/ice/ice_diagnose.c
index 0aa19eb282..2b9794d212 100644
--- a/drivers/net/ice/ice_ddp_package.c
+++ b/drivers/net/ice/ice_diagnose.c
@@ -11,6 +11,7 @@
 #include <rte_tailq.h>
 
 #include "ice_ethdev.h"
+#include "ice_rxtx.h"
 
 #define ICE_BLK_MAX_COUNT          512
 #define ICE_BUFF_SEG_HEADER_FLAG   0x1
@@ -507,3 +508,375 @@ int rte_pmd_ice_dump_switch(uint16_t port, uint8_t **buff, uint32_t *size)
 
 	return ice_dump_switch(dev, buff, size);
 }
+
+static void print_rl_profile(struct ice_aqc_rl_profile_elem *prof,
+			     FILE *stream)
+{
+	fprintf(stream, "\t\t\t\t\t<td>\n");
+	fprintf(stream, "\t\t\t\t\t\t<table>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>id</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->profile_id);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>max burst size</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->max_burst_size);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>rate limit multiply</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->rl_multiply);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>wake up calculation</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->wake_up_calc);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>rate limit encode</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->rl_encode);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t</table>\n");
+	fprintf(stream, "\t\t\t\t\t</td>\n");
+}
+
+static
+void print_elem_type(FILE *stream, u8 type)
+{
+	switch (type) {
+	case 1:
+		fprintf(stream, "root");
+		break;
+	case 2:
+		fprintf(stream, "tc");
+		break;
+	case 3:
+		fprintf(stream, "se_generic");
+		break;
+	case 4:
+		fprintf(stream, "entry_point");
+		break;
+	case 5:
+		fprintf(stream, "leaf");
+		break;
+	default:
+		fprintf(stream, "%d", type);
+		break;
+	}
+}
+
+static
+void print_valid_sections(FILE *stream, u8 vs)
+{
+	if ((vs & 0x1) != 0)
+		fprintf(stream, "generic ");
+	if ((vs & 0x2) != 0)
+		fprintf(stream, "cir ");
+	if ((vs & 0x4) != 0)
+		fprintf(stream, "eir ");
+	if ((vs & 0x8) != 0)
+		fprintf(stream, "shared ");
+}
+
+static
+void print_scheduling_mode(FILE *stream, bool flag)
+{
+	if (flag)
+		fprintf(stream, "pps");
+	else
+		fprintf(stream, "bps");
+}
+
+static
+void print_priority_mode(FILE *stream, bool flag)
+{
+	if (flag)
+		fprintf(stream, "single priority node");
+	else
+		fprintf(stream, "wfq");
+}
+
+static
+void print_node(struct ice_aqc_txsched_elem_data *data,
+		struct ice_aqc_rl_profile_elem *cir_prof,
+		struct ice_aqc_rl_profile_elem *eir_prof,
+		struct ice_aqc_rl_profile_elem *shared_prof,
+		bool detail, FILE *stream)
+{
+	fprintf(stream, "\tNODE_%d [\n", data->node_teid);
+	fprintf(stream, "\t\tlabel=<\n");
+
+	fprintf(stream, "\t\t\t<table>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> teid </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->node_teid);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> type </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_elem_type(stream, data->data.elem_type);
+	fprintf(stream, "</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	if (!detail)
+		goto brief;
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> valid sections </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_valid_sections(stream, data->data.valid_sections);
+	fprintf(stream,	"</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> scheduling mode </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_scheduling_mode(stream, (data->data.generic & 0x1) != 0);
+	fprintf(stream, "</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> priority </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", (data->data.generic >> 1) & 0x7);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> priority mode</td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_priority_mode(stream, ((data->data.generic >> 4) & 0x1) != 0);
+	fprintf(stream,	"</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> adjustment value </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", (data->data.generic >> 5) & 0x3);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> suspended </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.flags & 0x1);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> cir bw profile </td>\n");
+	if (cir_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(cir_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> cir bw weight </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.cir_bw.bw_alloc);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> eir bw profile </td>\n");
+	if (eir_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(eir_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> eir bw weight </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.eir_bw.bw_alloc);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> shared rl profile </td>\n");
+	if (shared_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(shared_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+brief:
+	fprintf(stream, "\t\t\t</table>\n");
+
+	fprintf(stream, "\t\t>\n");
+	fprintf(stream, "\t\tshape=plain\n");
+	fprintf(stream, "\t]\n");
+
+	if (data->parent_teid != 0xFFFFFFFF)
+		fprintf(stream, "\tNODE_%d -> NODE_%d\n", data->parent_teid, data->node_teid);
+}
+
+static
+int query_rl_profile(struct ice_hw *hw,
+		     uint8_t level, uint8_t flags, uint16_t profile_id,
+		     struct ice_aqc_rl_profile_elem *data)
+{
+	enum ice_status ice_status;
+
+	data->level = level;
+	data->flags = flags;
+	data->profile_id = profile_id;
+
+	ice_status = ice_aq_query_rl_profile(hw, 1, data,
+					     sizeof(struct ice_aqc_rl_profile_elem), NULL);
+
+	if (ice_status != ICE_SUCCESS) {
+		PMD_DRV_LOG(ERR, "Failed to query rl profile.");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static
+int query_node(struct ice_hw *hw, uint32_t child, uint32_t *parent,
+	       uint8_t level, bool detail, FILE *stream)
+{
+	struct ice_aqc_txsched_elem_data data;
+	enum ice_status status;
+	struct ice_aqc_rl_profile_elem cir_prof;
+	struct ice_aqc_rl_profile_elem eir_prof;
+	struct ice_aqc_rl_profile_elem shared_prof;
+	struct ice_aqc_rl_profile_elem *cp = NULL;
+	struct ice_aqc_rl_profile_elem *ep = NULL;
+	struct ice_aqc_rl_profile_elem *sp = NULL;
+	int ret;
+
+	status = ice_sched_query_elem(hw, child, &data);
+	if (status != ICE_SUCCESS) {
+		if (level == hw->num_tx_sched_layers) {
+			/* ignore the error when a queue has been stopped. */
+			PMD_DRV_LOG(WARNING, "Failed to query queue node %d.", child);
+			*parent = 0xffffffff;
+			return 0;
+		} else {
+			PMD_DRV_LOG(ERR, "Failed to query scheduling node %d.", child);
+			return -EINVAL;
+		}
+	}
+
+	*parent = data.parent_teid;
+
+	if (data.data.cir_bw.bw_profile_idx != 0) {
+		ret = query_rl_profile(hw, level, 0, data.data.cir_bw.bw_profile_idx, &cir_prof);
+
+		if (ret)
+			return ret;
+		cp = &cir_prof;
+	}
+
+	if (data.data.eir_bw.bw_profile_idx != 0) {
+		ret = query_rl_profile(hw, level, 1, data.data.eir_bw.bw_profile_idx, &eir_prof);
+
+		if (ret)
+			return ret;
+		ep = &eir_prof;
+	}
+
+	if (data.data.srl_id != 0) {
+		ret = query_rl_profile(hw, level, 2, data.data.srl_id, &shared_prof);
+
+		if (ret)
+			return ret;
+		sp = &shared_prof;
+	}
+
+	print_node(&data, cp, ep, sp, detail, stream);
+
+	return 0;
+}
+
+static
+int query_nodes(struct ice_hw *hw,
+		uint32_t *children, int child_num,
+		uint32_t *parents, int *parent_num,
+		uint8_t level, bool detail,
+		FILE *stream)
+{
+	uint32_t parent;
+	int i;
+	int j;
+
+	*parent_num = 0;
+	for (i = 0; i < child_num; i++) {
+		bool exist = false;
+		int ret;
+
+		ret = query_node(hw, children[i], &parent, level, detail, stream);
+		if (ret)
+			return -EINVAL;
+
+		for (j = 0; j < *parent_num; j++) {
+			if (parents[j] == parent) {
+				exist = true;
+				break;
+			}
+		}
+
+		if (!exist && parent != 0xFFFFFFFF)
+			parents[(*parent_num)++] = parent;
+	}
+
+	return 0;
+}
+
+int rte_pmd_ice_dump_txsched(uint16_t port, bool detail, FILE *stream)
+{
+	struct rte_eth_dev *dev;
+	struct ice_hw *hw;
+	struct ice_pf *pf;
+	struct ice_q_ctx *q_ctx;
+	uint16_t q_num;
+	uint16_t i;
+	struct ice_tx_queue *txq;
+	uint32_t buf1[256];
+	uint32_t buf2[256];
+	uint32_t *children = buf1;
+	uint32_t *parents = buf2;
+	int child_num = 0;
+	int parent_num = 0;
+	uint8_t level;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+	if (!is_ice_supported(dev))
+		return -ENOTSUP;
+
+	dev = &rte_eth_devices[port];
+	hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	pf = ICE_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	level = hw->num_tx_sched_layers;
+
+	q_num = dev->data->nb_tx_queues;
+
+	/* main vsi */
+	for (i = 0; i < q_num; i++) {
+		txq = dev->data->tx_queues[i];
+		q_ctx = ice_get_lan_q_ctx(hw, txq->vsi->idx, 0, i);
+		children[child_num++] = q_ctx->q_teid;
+	}
+
+	/* fdir vsi */
+	q_ctx = ice_get_lan_q_ctx(hw, pf->fdir.fdir_vsi->idx, 0, 0);
+	children[child_num++] = q_ctx->q_teid;
+
+	fprintf(stream, "digraph tx_sched {\n");
+	while (child_num > 0) {
+		int ret;
+		ret = query_nodes(hw, children, child_num,
+				  parents, &parent_num,
+				  level, detail, stream);
+		if (ret)
+			return ret;
+
+		children = parents;
+		child_num = parent_num;
+		level--;
+	}
+	fprintf(stream, "}\n");
+
+	return 0;
+}
diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index d607f028e0..1338c80d14 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -739,4 +739,7 @@ int rte_pmd_ice_dump_package(uint16_t port, uint8_t **buff, uint32_t *size);
 
 __rte_experimental
 int rte_pmd_ice_dump_switch(uint16_t port, uint8_t **buff, uint32_t *size);
+
+__rte_experimental
+int rte_pmd_ice_dump_txsched(uint16_t port, bool detail, FILE *stream);
 #endif /* _ICE_ETHDEV_H_ */
diff --git a/drivers/net/ice/ice_testpmd.c b/drivers/net/ice/ice_testpmd.c
index a7a8d0c53c..98c02d68c6 100644
--- a/drivers/net/ice/ice_testpmd.c
+++ b/drivers/net/ice/ice_testpmd.c
@@ -3,6 +3,7 @@
  */
 
 #include <stdlib.h>
+#include <stdio.h>
 
 #include <cmdline_parse_num.h>
 #include <cmdline_parse_string.h>
@@ -148,6 +149,63 @@ cmdline_parse_inst_t cmd_ddp_dump_switch = {
 	},
 };
 
+/* Dump Tx Scheduling Tree configuration, only for ice PF */
+struct cmd_txsched_dump_result {
+	cmdline_fixed_string_t txsched;
+	cmdline_fixed_string_t dump;
+	portid_t port_id;
+	cmdline_fixed_string_t mode;
+	char filepath[];
+};
+
+cmdline_parse_token_string_t cmd_txsched_dump_txsched =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, txsched, "txsched");
+cmdline_parse_token_string_t cmd_txsched_dump_dump =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, dump, "dump");
+cmdline_parse_token_num_t cmd_txsched_dump_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_txsched_dump_result, port_id, RTE_UINT16);
+cmdline_parse_token_string_t cmd_txsched_dump_mode =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, mode, "brief#detail");
+cmdline_parse_token_string_t cmd_txsched_dump_filepath =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, filepath, NULL);
+
+static void
+cmd_txsched_dump_parsed(void *parsed_result,
+			__rte_unused struct cmdline *cl,
+			__rte_unused void *data)
+{
+	struct cmd_txsched_dump_result *res = parsed_result;
+	bool detail = false;
+	FILE *fp;
+
+	if (!strcmp(res->mode, "detail"))
+		detail = true;
+
+	fp = fopen(res->filepath, "w");
+	if (fp == NULL) {
+		fprintf(stderr, "Failed to open file\n");
+		return;
+	}
+
+	if (rte_pmd_ice_dump_txsched(res->port_id, detail, fp))
+		fprintf(stderr, "Failed to dump Tx scheduring runtime configure.\n");
+	fclose(fp);
+}
+
+cmdline_parse_inst_t cmd_txsched_dump = {
+	.f = cmd_txsched_dump_parsed,
+	.data = NULL,
+	.help_str = "txsched dump <port_id> <brief|detail> <config_path>",
+	.tokens = {
+		(void *)&cmd_txsched_dump_txsched,
+		(void *)&cmd_txsched_dump_dump,
+		(void *)&cmd_txsched_dump_port_id,
+		(void *)&cmd_txsched_dump_mode,
+		(void *)&cmd_txsched_dump_filepath,
+		NULL,
+	},
+};
+
 static struct testpmd_driver_commands ice_cmds = {
 	.commands = {
 	{
@@ -161,8 +219,15 @@ static struct testpmd_driver_commands ice_cmds = {
 		"ddp dump switch (port_id) (config_path)\n"
 		"    Dump a runtime switch configure on a port\n\n",
 
+	},
+	{
+		&cmd_txsched_dump,
+		"txsched dump (port_id) <brief|detail> (config_path)\n"
+		"    Dump tx scheduling runtime configure on a port\n\n",
+
 	},
 	{ NULL, NULL },
 	},
 };
+
 TESTPMD_ADD_DRIVER_COMMANDS(ice_cmds)
diff --git a/drivers/net/ice/meson.build b/drivers/net/ice/meson.build
index a957fc5d3a..b7f2188e62 100644
--- a/drivers/net/ice/meson.build
+++ b/drivers/net/ice/meson.build
@@ -6,7 +6,7 @@ objs = [base_objs]
 
 sources = files(
         'ice_acl_filter.c',
-        'ice_ddp_package.c',
+        'ice_diagnose.c',
         'ice_ethdev.c',
         'ice_fdir_filter.c',
         'ice_generic_flow.c',
diff --git a/drivers/net/ice/version.map b/drivers/net/ice/version.map
index 4e924c8f4d..24b425d6f7 100644
--- a/drivers/net/ice/version.map
+++ b/drivers/net/ice/version.map
@@ -8,4 +8,5 @@ EXPERIMENTAL {
 	# added in 19.11
 	rte_pmd_ice_dump_package;
 	rte_pmd_ice_dump_switch;
+	rte_pmd_ice_dump_txsched;
 };
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v6 2/2] doc: add document for ice diagnostic utilities
  2024-01-02 12:29 ` [PATCH v6 " Qi Zhang
@ 2024-01-02 12:29   ` Qi Zhang
  2024-01-02 17:05     ` Stephen Hemminger
  0 siblings, 1 reply; 22+ messages in thread
From: Qi Zhang @ 2024-01-02 12:29 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Document CLI for diagnose purpose.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---

v6:
- fix title

 doc/guides/nics/ice.rst | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
index 820a385b06..29309abe4d 100644
--- a/doc/guides/nics/ice.rst
+++ b/doc/guides/nics/ice.rst
@@ -411,6 +411,42 @@ To start ``testpmd``, and add vlan 10 to port 0:
 
     testpmd> rx_vlan add 10 0
 
+Diagnostic Utilities
+--------------------
+
+Dump DDP Package
+~~~~~~~~~~~~~~~~
+
+Dump the runtime packet processing pipeline configuration into a
+binary file. This helps the support team diagnose hardware
+configuration issues.
+
+Usage::
+
+    testpmd>ddp dump <port_id> <output_file>
+
+Dump Switch Configurations
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Dump detail hardware configurations related to the switch pipeline
+stage into a binary file.
+
+Usage::
+
+    testpmd>ddp dump switch <port_id> <output_file>
+
+Dump Tx Scheduling Tree
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Dump the runtime Tx scheduling tree into a DOT file.
+
+Usage::
+
+    testpmd>txsched dump <port_id> <brief|detail> <output_file>
+
+In "brief" mode, all scheduling nodes in the tree are displayed.
+In "detail" mode, each node's configuration parameters are also displayed.
+
 Limitations or Known issues
 ---------------------------
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v7 1/2] net/ice:  add Tx scheduling tree dump support
  2023-12-26 18:54 [PATCH 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
                   ` (7 preceding siblings ...)
  2024-01-02 12:29 ` [PATCH v6 " Qi Zhang
@ 2024-01-02 12:39 ` Qi Zhang
  2024-01-02  6:22   ` Wu, Wenjun1
  2024-01-02 12:39   ` [PATCH v7 2/2] doc: add document for ice diagnostic utilities Qi Zhang
  8 siblings, 2 replies; 22+ messages in thread
From: Qi Zhang @ 2024-01-02 12:39 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Added Testpmd CLI support for dumping Tx scheduling tree.

Usage:
testpmd>txsched dump <port_id> <brief|detail> <filename>

The output file is in "dot" format, which can be converted
into an image file using Graphviz.

- In "brief" mode, all scheduling nodes in the tree are displayed.
- In "detail" mode, each node's configuration parameters are also
  displayed.

Renamed `ice_ddp_package.c` to `ice_diagnose.c`, which now contains
all CLI support for diagnostic purposes.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
v7:
- fix ci warning.

v5:
- ignore the error when query node failed at queue level, as queue may
   be stopped.
v4:
- show node type in brief mode

v3:
- fix incorrect parameter when query rl profile

v2:
- fix CI build issue

 .../ice/{ice_ddp_package.c => ice_diagnose.c} | 372 ++++++++++++++++++
 drivers/net/ice/ice_ethdev.h                  |   3 +
 drivers/net/ice/ice_testpmd.c                 |  65 +++
 drivers/net/ice/meson.build                   |   2 +-
 drivers/net/ice/version.map                   |   1 +
 5 files changed, 442 insertions(+), 1 deletion(-)
 rename drivers/net/ice/{ice_ddp_package.c => ice_diagnose.c} (60%)

diff --git a/drivers/net/ice/ice_ddp_package.c b/drivers/net/ice/ice_diagnose.c
similarity index 60%
rename from drivers/net/ice/ice_ddp_package.c
rename to drivers/net/ice/ice_diagnose.c
index 0aa19eb282..3be819d7f8 100644
--- a/drivers/net/ice/ice_ddp_package.c
+++ b/drivers/net/ice/ice_diagnose.c
@@ -11,6 +11,7 @@
 #include <rte_tailq.h>
 
 #include "ice_ethdev.h"
+#include "ice_rxtx.h"
 
 #define ICE_BLK_MAX_COUNT          512
 #define ICE_BUFF_SEG_HEADER_FLAG   0x1
@@ -507,3 +508,374 @@ int rte_pmd_ice_dump_switch(uint16_t port, uint8_t **buff, uint32_t *size)
 
 	return ice_dump_switch(dev, buff, size);
 }
+
+static void print_rl_profile(struct ice_aqc_rl_profile_elem *prof,
+			     FILE *stream)
+{
+	fprintf(stream, "\t\t\t\t\t<td>\n");
+	fprintf(stream, "\t\t\t\t\t\t<table>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>id</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->profile_id);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>max burst size</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->max_burst_size);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>rate limit multiply</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->rl_multiply);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>wake up calculation</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->wake_up_calc);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>rate limit encode</td>\n");
+	fprintf(stream, "\t\t\t\t\t\t\t\t<td>%d</td>\n", prof->rl_encode);
+	fprintf(stream, "\t\t\t\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t\t\t</table>\n");
+	fprintf(stream, "\t\t\t\t\t</td>\n");
+}
+
+static
+void print_elem_type(FILE *stream, u8 type)
+{
+	switch (type) {
+	case 1:
+		fprintf(stream, "root");
+		break;
+	case 2:
+		fprintf(stream, "tc");
+		break;
+	case 3:
+		fprintf(stream, "se_generic");
+		break;
+	case 4:
+		fprintf(stream, "entry_point");
+		break;
+	case 5:
+		fprintf(stream, "leaf");
+		break;
+	default:
+		fprintf(stream, "%d", type);
+		break;
+	}
+}
+
+static
+void print_valid_sections(FILE *stream, u8 vs)
+{
+	if ((vs & 0x1) != 0)
+		fprintf(stream, "generic ");
+	if ((vs & 0x2) != 0)
+		fprintf(stream, "cir ");
+	if ((vs & 0x4) != 0)
+		fprintf(stream, "eir ");
+	if ((vs & 0x8) != 0)
+		fprintf(stream, "shared ");
+}
+
+static
+void print_scheduling_mode(FILE *stream, bool flag)
+{
+	if (flag)
+		fprintf(stream, "pps");
+	else
+		fprintf(stream, "bps");
+}
+
+static
+void print_priority_mode(FILE *stream, bool flag)
+{
+	if (flag)
+		fprintf(stream, "single priority node");
+	else
+		fprintf(stream, "wfq");
+}
+
+static
+void print_node(struct ice_aqc_txsched_elem_data *data,
+		struct ice_aqc_rl_profile_elem *cir_prof,
+		struct ice_aqc_rl_profile_elem *eir_prof,
+		struct ice_aqc_rl_profile_elem *shared_prof,
+		bool detail, FILE *stream)
+{
+	fprintf(stream, "\tNODE_%d [\n", data->node_teid);
+	fprintf(stream, "\t\tlabel=<\n");
+
+	fprintf(stream, "\t\t\t<table>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> teid </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->node_teid);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> type </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_elem_type(stream, data->data.elem_type);
+	fprintf(stream, "</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	if (!detail)
+		goto brief;
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> valid sections </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_valid_sections(stream, data->data.valid_sections);
+	fprintf(stream,	"</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> scheduling mode </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_scheduling_mode(stream, (data->data.generic & 0x1) != 0);
+	fprintf(stream, "</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> priority </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", (data->data.generic >> 1) & 0x7);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> priority mode</td>\n");
+	fprintf(stream, "\t\t\t\t\t<td>");
+	print_priority_mode(stream, ((data->data.generic >> 4) & 0x1) != 0);
+	fprintf(stream,	"</td>\n");
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> adjustment value </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", (data->data.generic >> 5) & 0x3);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> suspended </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.flags & 0x1);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> cir bw profile </td>\n");
+	if (cir_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(cir_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> cir bw weight </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.cir_bw.bw_alloc);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> eir bw profile </td>\n");
+	if (eir_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(eir_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> eir bw weight </td>\n");
+	fprintf(stream, "\t\t\t\t\t<td> %d </td>\n", data->data.eir_bw.bw_alloc);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+	fprintf(stream, "\t\t\t\t<tr>\n");
+	fprintf(stream, "\t\t\t\t\t<td> shared rl profile </td>\n");
+	if (shared_prof == NULL)
+		fprintf(stream, "\t\t\t\t\t<td> default </td>\n");
+	else
+		print_rl_profile(shared_prof, stream);
+	fprintf(stream, "\t\t\t\t</tr>\n");
+
+brief:
+	fprintf(stream, "\t\t\t</table>\n");
+
+	fprintf(stream, "\t\t>\n");
+	fprintf(stream, "\t\tshape=plain\n");
+	fprintf(stream, "\t]\n");
+
+	if (data->parent_teid != 0xFFFFFFFF)
+		fprintf(stream, "\tNODE_%d -> NODE_%d\n", data->parent_teid, data->node_teid);
+}
+
+static
+int query_rl_profile(struct ice_hw *hw,
+		     uint8_t level, uint8_t flags, uint16_t profile_id,
+		     struct ice_aqc_rl_profile_elem *data)
+{
+	enum ice_status ice_status;
+
+	data->level = level;
+	data->flags = flags;
+	data->profile_id = profile_id;
+
+	ice_status = ice_aq_query_rl_profile(hw, 1, data,
+					     sizeof(struct ice_aqc_rl_profile_elem), NULL);
+
+	if (ice_status != ICE_SUCCESS) {
+		PMD_DRV_LOG(ERR, "Failed to query rl profile.");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static
+int query_node(struct ice_hw *hw, uint32_t child, uint32_t *parent,
+	       uint8_t level, bool detail, FILE *stream)
+{
+	struct ice_aqc_txsched_elem_data data;
+	enum ice_status status;
+	struct ice_aqc_rl_profile_elem cir_prof;
+	struct ice_aqc_rl_profile_elem eir_prof;
+	struct ice_aqc_rl_profile_elem shared_prof;
+	struct ice_aqc_rl_profile_elem *cp = NULL;
+	struct ice_aqc_rl_profile_elem *ep = NULL;
+	struct ice_aqc_rl_profile_elem *sp = NULL;
+	int ret;
+
+	status = ice_sched_query_elem(hw, child, &data);
+	if (status != ICE_SUCCESS) {
+		if (level == hw->num_tx_sched_layers) {
+			/* ignore the error when a queue has been stopped. */
+			PMD_DRV_LOG(WARNING, "Failed to query queue node %d.", child);
+			*parent = 0xffffffff;
+			return 0;
+		}
+		PMD_DRV_LOG(ERR, "Failed to query scheduling node %d.", child);
+		return -EINVAL;
+	}
+
+	*parent = data.parent_teid;
+
+	if (data.data.cir_bw.bw_profile_idx != 0) {
+		ret = query_rl_profile(hw, level, 0, data.data.cir_bw.bw_profile_idx, &cir_prof);
+
+		if (ret)
+			return ret;
+		cp = &cir_prof;
+	}
+
+	if (data.data.eir_bw.bw_profile_idx != 0) {
+		ret = query_rl_profile(hw, level, 1, data.data.eir_bw.bw_profile_idx, &eir_prof);
+
+		if (ret)
+			return ret;
+		ep = &eir_prof;
+	}
+
+	if (data.data.srl_id != 0) {
+		ret = query_rl_profile(hw, level, 2, data.data.srl_id, &shared_prof);
+
+		if (ret)
+			return ret;
+		sp = &shared_prof;
+	}
+
+	print_node(&data, cp, ep, sp, detail, stream);
+
+	return 0;
+}
+
+static
+int query_nodes(struct ice_hw *hw,
+		uint32_t *children, int child_num,
+		uint32_t *parents, int *parent_num,
+		uint8_t level, bool detail,
+		FILE *stream)
+{
+	uint32_t parent;
+	int i;
+	int j;
+
+	*parent_num = 0;
+	for (i = 0; i < child_num; i++) {
+		bool exist = false;
+		int ret;
+
+		ret = query_node(hw, children[i], &parent, level, detail, stream);
+		if (ret)
+			return -EINVAL;
+
+		for (j = 0; j < *parent_num; j++) {
+			if (parents[j] == parent) {
+				exist = true;
+				break;
+			}
+		}
+
+		if (!exist && parent != 0xFFFFFFFF)
+			parents[(*parent_num)++] = parent;
+	}
+
+	return 0;
+}
+
+int rte_pmd_ice_dump_txsched(uint16_t port, bool detail, FILE *stream)
+{
+	struct rte_eth_dev *dev;
+	struct ice_hw *hw;
+	struct ice_pf *pf;
+	struct ice_q_ctx *q_ctx;
+	uint16_t q_num;
+	uint16_t i;
+	struct ice_tx_queue *txq;
+	uint32_t buf1[256];
+	uint32_t buf2[256];
+	uint32_t *children = buf1;
+	uint32_t *parents = buf2;
+	int child_num = 0;
+	int parent_num = 0;
+	uint8_t level;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
+
+	dev = &rte_eth_devices[port];
+	if (!is_ice_supported(dev))
+		return -ENOTSUP;
+
+	dev = &rte_eth_devices[port];
+	hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	pf = ICE_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	level = hw->num_tx_sched_layers;
+
+	q_num = dev->data->nb_tx_queues;
+
+	/* main vsi */
+	for (i = 0; i < q_num; i++) {
+		txq = dev->data->tx_queues[i];
+		q_ctx = ice_get_lan_q_ctx(hw, txq->vsi->idx, 0, i);
+		children[child_num++] = q_ctx->q_teid;
+	}
+
+	/* fdir vsi */
+	q_ctx = ice_get_lan_q_ctx(hw, pf->fdir.fdir_vsi->idx, 0, 0);
+	children[child_num++] = q_ctx->q_teid;
+
+	fprintf(stream, "digraph tx_sched {\n");
+	while (child_num > 0) {
+		int ret;
+		ret = query_nodes(hw, children, child_num,
+				  parents, &parent_num,
+				  level, detail, stream);
+		if (ret)
+			return ret;
+
+		children = parents;
+		child_num = parent_num;
+		level--;
+	}
+	fprintf(stream, "}\n");
+
+	return 0;
+}
diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index d607f028e0..1338c80d14 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -739,4 +739,7 @@ int rte_pmd_ice_dump_package(uint16_t port, uint8_t **buff, uint32_t *size);
 
 __rte_experimental
 int rte_pmd_ice_dump_switch(uint16_t port, uint8_t **buff, uint32_t *size);
+
+__rte_experimental
+int rte_pmd_ice_dump_txsched(uint16_t port, bool detail, FILE *stream);
 #endif /* _ICE_ETHDEV_H_ */
diff --git a/drivers/net/ice/ice_testpmd.c b/drivers/net/ice/ice_testpmd.c
index a7a8d0c53c..98c02d68c6 100644
--- a/drivers/net/ice/ice_testpmd.c
+++ b/drivers/net/ice/ice_testpmd.c
@@ -3,6 +3,7 @@
  */
 
 #include <stdlib.h>
+#include <stdio.h>
 
 #include <cmdline_parse_num.h>
 #include <cmdline_parse_string.h>
@@ -148,6 +149,63 @@ cmdline_parse_inst_t cmd_ddp_dump_switch = {
 	},
 };
 
+/* Dump Tx Scheduling Tree configuration, only for ice PF */
+struct cmd_txsched_dump_result {
+	cmdline_fixed_string_t txsched;
+	cmdline_fixed_string_t dump;
+	portid_t port_id;
+	cmdline_fixed_string_t mode;
+	char filepath[];
+};
+
+cmdline_parse_token_string_t cmd_txsched_dump_txsched =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, txsched, "txsched");
+cmdline_parse_token_string_t cmd_txsched_dump_dump =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, dump, "dump");
+cmdline_parse_token_num_t cmd_txsched_dump_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_txsched_dump_result, port_id, RTE_UINT16);
+cmdline_parse_token_string_t cmd_txsched_dump_mode =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, mode, "brief#detail");
+cmdline_parse_token_string_t cmd_txsched_dump_filepath =
+	TOKEN_STRING_INITIALIZER(struct cmd_txsched_dump_result, filepath, NULL);
+
+static void
+cmd_txsched_dump_parsed(void *parsed_result,
+			__rte_unused struct cmdline *cl,
+			__rte_unused void *data)
+{
+	struct cmd_txsched_dump_result *res = parsed_result;
+	bool detail = false;
+	FILE *fp;
+
+	if (!strcmp(res->mode, "detail"))
+		detail = true;
+
+	fp = fopen(res->filepath, "w");
+	if (fp == NULL) {
+		fprintf(stderr, "Failed to open file\n");
+		return;
+	}
+
+	if (rte_pmd_ice_dump_txsched(res->port_id, detail, fp))
+		fprintf(stderr, "Failed to dump Tx scheduring runtime configure.\n");
+	fclose(fp);
+}
+
+cmdline_parse_inst_t cmd_txsched_dump = {
+	.f = cmd_txsched_dump_parsed,
+	.data = NULL,
+	.help_str = "txsched dump <port_id> <brief|detail> <config_path>",
+	.tokens = {
+		(void *)&cmd_txsched_dump_txsched,
+		(void *)&cmd_txsched_dump_dump,
+		(void *)&cmd_txsched_dump_port_id,
+		(void *)&cmd_txsched_dump_mode,
+		(void *)&cmd_txsched_dump_filepath,
+		NULL,
+	},
+};
+
 static struct testpmd_driver_commands ice_cmds = {
 	.commands = {
 	{
@@ -161,8 +219,15 @@ static struct testpmd_driver_commands ice_cmds = {
 		"ddp dump switch (port_id) (config_path)\n"
 		"    Dump a runtime switch configure on a port\n\n",
 
+	},
+	{
+		&cmd_txsched_dump,
+		"txsched dump (port_id) <brief|detail> (config_path)\n"
+		"    Dump tx scheduling runtime configure on a port\n\n",
+
 	},
 	{ NULL, NULL },
 	},
 };
+
 TESTPMD_ADD_DRIVER_COMMANDS(ice_cmds)
diff --git a/drivers/net/ice/meson.build b/drivers/net/ice/meson.build
index a957fc5d3a..b7f2188e62 100644
--- a/drivers/net/ice/meson.build
+++ b/drivers/net/ice/meson.build
@@ -6,7 +6,7 @@ objs = [base_objs]
 
 sources = files(
         'ice_acl_filter.c',
-        'ice_ddp_package.c',
+        'ice_diagnose.c',
         'ice_ethdev.c',
         'ice_fdir_filter.c',
         'ice_generic_flow.c',
diff --git a/drivers/net/ice/version.map b/drivers/net/ice/version.map
index 4e924c8f4d..24b425d6f7 100644
--- a/drivers/net/ice/version.map
+++ b/drivers/net/ice/version.map
@@ -8,4 +8,5 @@ EXPERIMENTAL {
 	# added in 19.11
 	rte_pmd_ice_dump_package;
 	rte_pmd_ice_dump_switch;
+	rte_pmd_ice_dump_txsched;
 };
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v7 2/2] doc: add document for ice diagnostic utilities
  2024-01-02 12:39 ` [PATCH v7 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
  2024-01-02  6:22   ` Wu, Wenjun1
@ 2024-01-02 12:39   ` Qi Zhang
  2024-01-02  7:14     ` Yang, Qiming
  1 sibling, 1 reply; 22+ messages in thread
From: Qi Zhang @ 2024-01-02 12:39 UTC (permalink / raw)
  To: qiming.yang, wenjun1.wu; +Cc: dev, Qi Zhang

Document ice specific testpmd CLI for diagnose purpose.

Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
---
v6:
- fix title

 doc/guides/nics/ice.rst | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
index 820a385b06..29309abe4d 100644
--- a/doc/guides/nics/ice.rst
+++ b/doc/guides/nics/ice.rst
@@ -411,6 +411,42 @@ To start ``testpmd``, and add vlan 10 to port 0:
 
     testpmd> rx_vlan add 10 0
 
+Diagnostic Utilities
+--------------------
+
+Dump DDP Package
+~~~~~~~~~~~~~~~~
+
+Dump the runtime packet processing pipeline configuration into a
+binary file. This helps the support team diagnose hardware
+configuration issues.
+
+Usage::
+
+    testpmd>ddp dump <port_id> <output_file>
+
+Dump Switch Configurations
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Dump detail hardware configurations related to the switch pipeline
+stage into a binary file.
+
+Usage::
+
+    testpmd>ddp dump switch <port_id> <output_file>
+
+Dump Tx Scheduling Tree
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Dump the runtime Tx scheduling tree into a DOT file.
+
+Usage::
+
+    testpmd>txsched dump <port_id> <brief|detail> <output_file>
+
+In "brief" mode, all scheduling nodes in the tree are displayed.
+In "detail" mode, each node's configuration parameters are also displayed.
+
 Limitations or Known issues
 ---------------------------
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v6 2/2] doc: add document for ice diagnostic utilities
  2024-01-02 12:29   ` [PATCH v6 2/2] doc: add document for ice diagnostic utilities Qi Zhang
@ 2024-01-02 17:05     ` Stephen Hemminger
  0 siblings, 0 replies; 22+ messages in thread
From: Stephen Hemminger @ 2024-01-02 17:05 UTC (permalink / raw)
  To: Qi Zhang; +Cc: qiming.yang, wenjun1.wu, dev

On Tue,  2 Jan 2024 07:29:31 -0500
Qi Zhang <qi.z.zhang@intel.com> wrote:

> Document CLI for diagnose purpose.
> 
> Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>

This raises the question for me.
Why is this only ice driver specific? Other drivers may implement similar features.

DPDK drivers are not supposed to be unique snowflakes.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v5 1/2] net/ice:  add Tx scheduling tree dump support
  2024-01-02 12:24 ` [PATCH v5 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
  2024-01-02 12:24   ` [PATCH v5 2/2] doc: add document for diagnostic utilities Qi Zhang
@ 2024-01-02 23:30   ` Stephen Hemminger
  1 sibling, 0 replies; 22+ messages in thread
From: Stephen Hemminger @ 2024-01-02 23:30 UTC (permalink / raw)
  To: Qi Zhang; +Cc: qiming.yang, wenjun1.wu, dev

On Tue,  2 Jan 2024 07:24:24 -0500
Qi Zhang <qi.z.zhang@intel.com> wrote:

> Added Testpmd CLI support for dumping Tx scheduling tree.
> 
> Usage:
> testpmd>txsched dump <port_id> <brief|detail> <filename>  
> 
> The output file is in "dot" format, which can be converted
> into an image file using Graphviz.
> 
> - In "brief" mode, all scheduling nodes in the tree are displayed.
> - In "detail" mode, each node's configuration parameters are also
>   displayed.
> 
> Renamed `ice_ddp_package.c` to `ice_diagnose.c`, which now contains
> all CLI support for diagnostic purposes.
> 
> Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>

Why not use json, which is the defacto for easy to parse text formats?

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2024-01-02 23:30 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-26 18:54 [PATCH 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
2023-12-26 18:54 ` [PATCH 2/2] doc: add document for diagnostic utilities Qi Zhang
2023-12-27 12:31 ` [PATCH v2 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
2023-12-27 12:31   ` [PATCH v2 2/2] doc: add document for diagnostic utilities Qi Zhang
2023-12-28 10:10 ` [PATCH v3 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
2023-12-28 10:10   ` [PATCH v3 2/2] doc: add document for diagnostic utilities Qi Zhang
2024-01-02 10:28 ` [PATCH v4 1/2] net/ice: reset Tx sched node during commit Qi Zhang
2024-01-02 10:29   ` [PATCH v4 2/2] net/ice: support Tx sched commit before dev_start Qi Zhang
2024-01-02 10:32 ` [PATCH 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
2024-01-02 10:32   ` [PATCH 2/2] doc: add document for diagnostic utilities Qi Zhang
2024-01-02 10:33 ` [PATCH v4 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
2024-01-02 10:33   ` [PATCH v4 2/2] doc: add document for diagnostic utilities Qi Zhang
2024-01-02 12:24 ` [PATCH v5 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
2024-01-02 12:24   ` [PATCH v5 2/2] doc: add document for diagnostic utilities Qi Zhang
2024-01-02 23:30   ` [PATCH v5 1/2] net/ice: add Tx scheduling tree dump support Stephen Hemminger
2024-01-02 12:29 ` [PATCH v6 " Qi Zhang
2024-01-02 12:29   ` [PATCH v6 2/2] doc: add document for ice diagnostic utilities Qi Zhang
2024-01-02 17:05     ` Stephen Hemminger
2024-01-02 12:39 ` [PATCH v7 1/2] net/ice: add Tx scheduling tree dump support Qi Zhang
2024-01-02  6:22   ` Wu, Wenjun1
2024-01-02 12:39   ` [PATCH v7 2/2] doc: add document for ice diagnostic utilities Qi Zhang
2024-01-02  7:14     ` Yang, Qiming

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).