[v3,1/2] net/ice: add Tx scheduling tree dump support

Message ID 20231228101032.3278127-1-qi.z.zhang@intel.com (mailing list archive)
State Superseded, archived
Delegated to: Qi Zhang
Headers
Series [v3,1/2] net/ice: add Tx scheduling tree dump support |

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

Qi Zhang Dec. 28, 2023, 10:10 a.m. UTC
  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%)
  

Patch

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;
 };