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.zh...@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