/* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2010-2017 Intel Corporation */ #include #include #include #include #include "rte_ethdev.h" #include "ethdev_driver.h" #include "sff_telemetry.h" #include "rte_tm.h" static const struct { uint32_t capa; const char *name; } rte_eth_fec_capa_name[] = { { RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC), "off" }, { RTE_ETH_FEC_MODE_CAPA_MASK(AUTO), "auto" }, { RTE_ETH_FEC_MODE_CAPA_MASK(BASER), "baser" }, { RTE_ETH_FEC_MODE_CAPA_MASK(RS), "rs" }, { RTE_ETH_FEC_MODE_CAPA_MASK(LLRS), "llrs" }, }; static int eth_dev_parse_port_params(const char *params, uint16_t *port_id, char **end_param, bool has_next) { uint64_t pi; if (params == NULL || strlen(params) == 0 || !isdigit(*params) || port_id == NULL) return -EINVAL; pi = strtoul(params, end_param, 0); if (**end_param != '\0' && !has_next) RTE_ETHDEV_LOG_LINE(NOTICE, "Extra parameters passed to ethdev telemetry command, ignoring"); if (pi >= UINT16_MAX || !rte_eth_dev_is_valid_port(pi)) return -EINVAL; *port_id = (uint16_t)pi; return 0; } static int eth_dev_handle_port_list(const char *cmd __rte_unused, const char *params __rte_unused, struct rte_tel_data *d) { int port_id; rte_tel_data_start_array(d, RTE_TEL_INT_VAL); RTE_ETH_FOREACH_DEV(port_id) rte_tel_data_add_array_int(d, port_id); return 0; } static void eth_dev_add_port_queue_stats(struct rte_tel_data *d, uint64_t *q_stats, const char *stat_name) { int q; struct rte_tel_data *q_data = rte_tel_data_alloc(); if (q_data == NULL) return; rte_tel_data_start_array(q_data, RTE_TEL_UINT_VAL); for (q = 0; q < RTE_ETHDEV_QUEUE_STAT_CNTRS; q++) rte_tel_data_add_array_uint(q_data, q_stats[q]); rte_tel_data_add_dict_container(d, stat_name, q_data, 0); } static int eth_dev_parse_hide_zero(const char *key, const char *value, void *extra_args) { RTE_SET_USED(key); if (value == NULL) return -1; if (strcmp(value, "true") == 0) *(bool *)extra_args = true; else if (strcmp(value, "false") == 0) *(bool *)extra_args = false; else return -1; return 0; } #define ADD_DICT_STAT(stats, s) rte_tel_data_add_dict_uint(d, #s, stats.s) static int eth_dev_handle_port_stats(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { struct rte_eth_stats stats; uint16_t port_id; char *end_param; int ret; ret = eth_dev_parse_port_params(params, &port_id, &end_param, false); if (ret < 0) return ret; ret = rte_eth_stats_get(port_id, &stats); if (ret < 0) return -1; rte_tel_data_start_dict(d); ADD_DICT_STAT(stats, ipackets); ADD_DICT_STAT(stats, opackets); ADD_DICT_STAT(stats, ibytes); ADD_DICT_STAT(stats, obytes); ADD_DICT_STAT(stats, imissed); ADD_DICT_STAT(stats, ierrors); ADD_DICT_STAT(stats, oerrors); ADD_DICT_STAT(stats, rx_nombuf); eth_dev_add_port_queue_stats(d, stats.q_ipackets, "q_ipackets"); eth_dev_add_port_queue_stats(d, stats.q_opackets, "q_opackets"); eth_dev_add_port_queue_stats(d, stats.q_ibytes, "q_ibytes"); eth_dev_add_port_queue_stats(d, stats.q_obytes, "q_obytes"); eth_dev_add_port_queue_stats(d, stats.q_errors, "q_errors"); return 0; } static int eth_dev_handle_port_xstats(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { const char *const valid_keys[] = { "hide_zero", NULL }; struct rte_eth_xstat *eth_xstats; struct rte_eth_xstat_name *xstat_names; struct rte_kvargs *kvlist; bool hide_zero = false; uint16_t port_id; char *end_param; int num_xstats; int i, ret; ret = eth_dev_parse_port_params(params, &port_id, &end_param, true); if (ret < 0) return ret; if (*end_param != '\0') { kvlist = rte_kvargs_parse(end_param, valid_keys); ret = rte_kvargs_process(kvlist, NULL, eth_dev_parse_hide_zero, &hide_zero); if (kvlist == NULL || ret != 0) RTE_ETHDEV_LOG_LINE(NOTICE, "Unknown extra parameters passed to ethdev telemetry command, ignoring"); rte_kvargs_free(kvlist); } num_xstats = rte_eth_xstats_get(port_id, NULL, 0); if (num_xstats < 0) return -1; /* use one malloc for both names and stats */ eth_xstats = malloc((sizeof(struct rte_eth_xstat) + sizeof(struct rte_eth_xstat_name)) * num_xstats); if (eth_xstats == NULL) return -1; xstat_names = (void *)ð_xstats[num_xstats]; ret = rte_eth_xstats_get_names(port_id, xstat_names, num_xstats); if (ret < 0 || ret > num_xstats) { free(eth_xstats); return -1; } ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats); if (ret < 0 || ret > num_xstats) { free(eth_xstats); return -1; } rte_tel_data_start_dict(d); for (i = 0; i < num_xstats; i++) { if (hide_zero && eth_xstats[i].value == 0) continue; rte_tel_data_add_dict_uint(d, xstat_names[i].name, eth_xstats[i].value); } free(eth_xstats); return 0; } #ifndef RTE_EXEC_ENV_WINDOWS static int eth_dev_handle_port_dump_priv(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { char *buf, *end_param; uint16_t port_id; int ret; FILE *f; ret = eth_dev_parse_port_params(params, &port_id, &end_param, false); if (ret < 0) return ret; buf = calloc(RTE_TEL_MAX_SINGLE_STRING_LEN, sizeof(char)); if (buf == NULL) return -ENOMEM; f = fmemopen(buf, RTE_TEL_MAX_SINGLE_STRING_LEN - 1, "w+"); if (f == NULL) { free(buf); return -EINVAL; } ret = rte_eth_dev_priv_dump(port_id, f); fclose(f); if (ret == 0) { rte_tel_data_start_dict(d); rte_tel_data_string(d, buf); } free(buf); return 0; } #endif /* !RTE_EXEC_ENV_WINDOWS */ static int eth_dev_handle_port_link_status(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { static const char *status_str = "status"; struct rte_eth_link link; uint16_t port_id; char *end_param; int ret; ret = eth_dev_parse_port_params(params, &port_id, &end_param, false); if (ret < 0) return ret; ret = rte_eth_link_get_nowait(port_id, &link); if (ret < 0) return -1; rte_tel_data_start_dict(d); if (!link.link_status) { rte_tel_data_add_dict_string(d, status_str, "DOWN"); return 0; } rte_tel_data_add_dict_string(d, status_str, "UP"); rte_tel_data_add_dict_uint(d, "speed", link.link_speed); rte_tel_data_add_dict_string(d, "duplex", (link.link_duplex == RTE_ETH_LINK_FULL_DUPLEX) ? "full-duplex" : "half-duplex"); return 0; } static void eth_dev_parse_rx_offloads(uint64_t offload, struct rte_tel_data *d) { uint64_t i; rte_tel_data_start_array(d, RTE_TEL_STRING_VAL); for (i = 0; i < CHAR_BIT * sizeof(offload); i++) { if ((offload & RTE_BIT64(i)) != 0) rte_tel_data_add_array_string(d, rte_eth_dev_rx_offload_name(offload & RTE_BIT64(i))); } } static void eth_dev_parse_tx_offloads(uint64_t offload, struct rte_tel_data *d) { uint64_t i; rte_tel_data_start_array(d, RTE_TEL_STRING_VAL); for (i = 0; i < CHAR_BIT * sizeof(offload); i++) { if ((offload & RTE_BIT64(i)) != 0) rte_tel_data_add_array_string(d, rte_eth_dev_tx_offload_name(offload & RTE_BIT64(i))); } } static int eth_dev_handle_port_info(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { struct rte_tel_data *rx_offload, *tx_offload; struct rte_tel_data *rxq_state, *txq_state; char fw_version[RTE_TEL_MAX_STRING_LEN]; char mac_addr[RTE_ETHER_ADDR_FMT_SIZE]; struct rte_eth_dev *eth_dev; uint16_t port_id; char *end_param; int ret; int i; ret = eth_dev_parse_port_params(params, &port_id, &end_param, false); if (ret < 0) return ret; eth_dev = &rte_eth_devices[port_id]; rxq_state = rte_tel_data_alloc(); if (rxq_state == NULL) return -ENOMEM; txq_state = rte_tel_data_alloc(); if (txq_state == NULL) goto free_rxq_state; rx_offload = rte_tel_data_alloc(); if (rx_offload == NULL) goto free_txq_state; tx_offload = rte_tel_data_alloc(); if (tx_offload == NULL) goto free_rx_offload; rte_tel_data_start_dict(d); rte_tel_data_add_dict_string(d, "name", eth_dev->data->name); if (rte_eth_dev_fw_version_get(port_id, fw_version, RTE_TEL_MAX_STRING_LEN) == 0) rte_tel_data_add_dict_string(d, "fw_version", fw_version); rte_tel_data_add_dict_int(d, "state", eth_dev->state); rte_tel_data_add_dict_int(d, "nb_rx_queues", eth_dev->data->nb_rx_queues); rte_tel_data_add_dict_int(d, "nb_tx_queues", eth_dev->data->nb_tx_queues); rte_tel_data_add_dict_int(d, "port_id", eth_dev->data->port_id); rte_tel_data_add_dict_int(d, "mtu", eth_dev->data->mtu); rte_tel_data_add_dict_uint(d, "rx_mbuf_size_min", eth_dev->data->min_rx_buf_size); rte_ether_format_addr(mac_addr, sizeof(mac_addr), eth_dev->data->mac_addrs); rte_tel_data_add_dict_string(d, "mac_addr", mac_addr); rte_tel_data_add_dict_int(d, "promiscuous", eth_dev->data->promiscuous); rte_tel_data_add_dict_int(d, "scattered_rx", eth_dev->data->scattered_rx); rte_tel_data_add_dict_int(d, "all_multicast", eth_dev->data->all_multicast); rte_tel_data_add_dict_int(d, "dev_started", eth_dev->data->dev_started); rte_tel_data_add_dict_int(d, "lro", eth_dev->data->lro); rte_tel_data_add_dict_int(d, "dev_configured", eth_dev->data->dev_configured); rte_tel_data_start_array(rxq_state, RTE_TEL_INT_VAL); for (i = 0; i < eth_dev->data->nb_rx_queues; i++) rte_tel_data_add_array_int(rxq_state, eth_dev->data->rx_queue_state[i]); rte_tel_data_start_array(txq_state, RTE_TEL_INT_VAL); for (i = 0; i < eth_dev->data->nb_tx_queues; i++) rte_tel_data_add_array_int(txq_state, eth_dev->data->tx_queue_state[i]); rte_tel_data_add_dict_container(d, "rxq_state", rxq_state, 0); rte_tel_data_add_dict_container(d, "txq_state", txq_state, 0); rte_tel_data_add_dict_int(d, "numa_node", eth_dev->data->numa_node); rte_tel_data_add_dict_uint_hex(d, "dev_flags", eth_dev->data->dev_flags, 0); eth_dev_parse_rx_offloads(eth_dev->data->dev_conf.rxmode.offloads, rx_offload); rte_tel_data_add_dict_container(d, "rx_offloads", rx_offload, 0); eth_dev_parse_tx_offloads(eth_dev->data->dev_conf.txmode.offloads, tx_offload); rte_tel_data_add_dict_container(d, "tx_offloads", tx_offload, 0); rte_tel_data_add_dict_uint_hex(d, "ethdev_rss_hf", eth_dev->data->dev_conf.rx_adv_conf.rss_conf.rss_hf, 0); return 0; free_rx_offload: rte_tel_data_free(rx_offload); free_txq_state: rte_tel_data_free(txq_state); free_rxq_state: rte_tel_data_free(rxq_state); return -ENOMEM; } static int eth_dev_handle_port_macs(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { char mac_addr[RTE_ETHER_ADDR_FMT_SIZE]; struct rte_eth_dev_info dev_info; struct rte_eth_dev *eth_dev; uint16_t port_id; char *end_param; uint32_t i; int ret; ret = eth_dev_parse_port_params(params, &port_id, &end_param, false); if (ret < 0) return ret; ret = rte_eth_dev_info_get(port_id, &dev_info); if (ret != 0) return ret; eth_dev = &rte_eth_devices[port_id]; rte_tel_data_start_array(d, RTE_TEL_STRING_VAL); for (i = 0; i < dev_info.max_mac_addrs; i++) { if (rte_is_zero_ether_addr(ð_dev->data->mac_addrs[i])) continue; rte_ether_format_addr(mac_addr, sizeof(mac_addr), ð_dev->data->mac_addrs[i]); rte_tel_data_add_array_string(d, mac_addr); } return 0; } static int eth_dev_handle_port_flow_ctrl(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { struct rte_eth_fc_conf fc_conf; uint16_t port_id; char *end_param; bool rx_fc_en; bool tx_fc_en; int ret; ret = eth_dev_parse_port_params(params, &port_id, &end_param, false); if (ret < 0) return ret; ret = rte_eth_dev_flow_ctrl_get(port_id, &fc_conf); if (ret != 0) { RTE_ETHDEV_LOG_LINE(ERR, "Failed to get flow ctrl info, ret = %d", ret); return ret; } rx_fc_en = fc_conf.mode == RTE_ETH_FC_RX_PAUSE || fc_conf.mode == RTE_ETH_FC_FULL; tx_fc_en = fc_conf.mode == RTE_ETH_FC_TX_PAUSE || fc_conf.mode == RTE_ETH_FC_FULL; rte_tel_data_start_dict(d); rte_tel_data_add_dict_uint_hex(d, "high_waterline", fc_conf.high_water, 0); rte_tel_data_add_dict_uint_hex(d, "low_waterline", fc_conf.low_water, 0); rte_tel_data_add_dict_uint_hex(d, "pause_time", fc_conf.pause_time, 0); rte_tel_data_add_dict_string(d, "send_xon", fc_conf.send_xon ? "on" : "off"); rte_tel_data_add_dict_string(d, "mac_ctrl_frame_fwd", fc_conf.mac_ctrl_frame_fwd ? "on" : "off"); rte_tel_data_add_dict_string(d, "rx_pause", rx_fc_en ? "on" : "off"); rte_tel_data_add_dict_string(d, "tx_pause", tx_fc_en ? "on" : "off"); rte_tel_data_add_dict_string(d, "autoneg", fc_conf.autoneg ? "on" : "off"); return 0; } static int ethdev_parse_queue_params(const char *params, bool is_rx, uint16_t *port_id, uint16_t *queue_id) { struct rte_eth_dev *dev; const char *qid_param; uint16_t nb_queues; char *end_param; uint64_t qid; int ret; ret = eth_dev_parse_port_params(params, port_id, &end_param, true); if (ret < 0) return ret; dev = &rte_eth_devices[*port_id]; nb_queues = is_rx ? dev->data->nb_rx_queues : dev->data->nb_tx_queues; if (nb_queues == 1 && *end_param == '\0') qid = 0; else { qid_param = strtok(end_param, ","); if (!qid_param || strlen(qid_param) == 0 || !isdigit(*qid_param)) return -EINVAL; qid = strtoul(qid_param, &end_param, 0); } if (*end_param != '\0') RTE_ETHDEV_LOG_LINE(NOTICE, "Extra parameters passed to ethdev telemetry command, ignoring"); if (qid >= UINT16_MAX) return -EINVAL; *queue_id = qid; return 0; } static int eth_dev_add_burst_mode(uint16_t port_id, uint16_t queue_id, bool is_rx, struct rte_tel_data *d) { struct rte_eth_burst_mode mode; int ret; if (is_rx) ret = rte_eth_rx_burst_mode_get(port_id, queue_id, &mode); else ret = rte_eth_tx_burst_mode_get(port_id, queue_id, &mode); if (ret == -ENOTSUP) return 0; if (ret != 0) { RTE_ETHDEV_LOG_LINE(ERR, "Failed to get burst mode for port %u", port_id); return ret; } rte_tel_data_add_dict_uint(d, "burst_flags", mode.flags); rte_tel_data_add_dict_string(d, "burst_mode", mode.info); return 0; } static int eth_dev_handle_port_rxq(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { struct rte_eth_thresh *rx_thresh; struct rte_eth_rxconf *rxconf; struct rte_eth_rxq_info qinfo; struct rte_tel_data *offload; uint16_t port_id, queue_id; int ret; ret = ethdev_parse_queue_params(params, true, &port_id, &queue_id); if (ret != 0) return ret; ret = rte_eth_rx_queue_info_get(port_id, queue_id, &qinfo); if (ret != 0) return ret; rte_tel_data_start_dict(d); rte_tel_data_add_dict_string(d, "mempool_name", qinfo.mp->name); rte_tel_data_add_dict_uint(d, "socket_id", qinfo.mp->socket_id); rx_thresh = &qinfo.conf.rx_thresh; rte_tel_data_add_dict_uint(d, "host_threshold", rx_thresh->hthresh); rte_tel_data_add_dict_uint(d, "prefetch_threshold", rx_thresh->pthresh); rte_tel_data_add_dict_uint(d, "writeback_threshold", rx_thresh->wthresh); rxconf = &qinfo.conf; rte_tel_data_add_dict_uint(d, "free_threshold", rxconf->rx_free_thresh); rte_tel_data_add_dict_string(d, "rx_drop_en", rxconf->rx_drop_en == 0 ? "off" : "on"); rte_tel_data_add_dict_string(d, "deferred_start", rxconf->rx_deferred_start == 0 ? "off" : "on"); rte_tel_data_add_dict_uint(d, "rx_nseg", rxconf->rx_nseg); rte_tel_data_add_dict_uint(d, "share_group", rxconf->share_group); rte_tel_data_add_dict_uint(d, "share_qid", rxconf->share_qid); offload = rte_tel_data_alloc(); if (offload == NULL) return -ENOMEM; eth_dev_parse_rx_offloads(rxconf->offloads, offload); rte_tel_data_add_dict_container(d, "offloads", offload, 0); rte_tel_data_add_dict_uint(d, "rx_nmempool", rxconf->rx_nmempool); rte_tel_data_add_dict_string(d, "scattered_rx", qinfo.scattered_rx == 0 ? "off" : "on"); rte_tel_data_add_dict_uint(d, "queue_state", qinfo.queue_state); rte_tel_data_add_dict_uint(d, "nb_desc", qinfo.nb_desc); rte_tel_data_add_dict_uint(d, "rx_buf_size", qinfo.rx_buf_size); rte_tel_data_add_dict_uint(d, "avail_thresh", qinfo.avail_thresh); ret = eth_dev_add_burst_mode(port_id, queue_id, true, d); if (ret != 0) rte_tel_data_free(offload); return ret; } static int eth_dev_handle_port_txq(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { struct rte_eth_thresh *tx_thresh; struct rte_eth_txconf *txconf; struct rte_eth_txq_info qinfo; struct rte_tel_data *offload; uint16_t port_id, queue_id; int ret; ret = ethdev_parse_queue_params(params, false, &port_id, &queue_id); if (ret != 0) return ret; ret = rte_eth_tx_queue_info_get(port_id, queue_id, &qinfo); if (ret != 0) return ret; rte_tel_data_start_dict(d); tx_thresh = &qinfo.conf.tx_thresh; txconf = &qinfo.conf; rte_tel_data_add_dict_uint(d, "host_threshold", tx_thresh->hthresh); rte_tel_data_add_dict_uint(d, "prefetch_threshold", tx_thresh->pthresh); rte_tel_data_add_dict_uint(d, "writeback_threshold", tx_thresh->wthresh); rte_tel_data_add_dict_uint(d, "rs_threshold", txconf->tx_rs_thresh); rte_tel_data_add_dict_uint(d, "free_threshold", txconf->tx_free_thresh); rte_tel_data_add_dict_string(d, "deferred_start", txconf->tx_deferred_start == 0 ? "off" : "on"); offload = rte_tel_data_alloc(); if (offload == NULL) return -ENOMEM; eth_dev_parse_tx_offloads(txconf->offloads, offload); rte_tel_data_add_dict_container(d, "offloads", offload, 0); rte_tel_data_add_dict_uint(d, "queue_state", qinfo.queue_state); rte_tel_data_add_dict_uint(d, "nb_desc", qinfo.nb_desc); ret = eth_dev_add_burst_mode(port_id, queue_id, false, d); if (ret != 0) rte_tel_data_free(offload); return 0; } static int eth_dev_add_dcb_tc(struct rte_eth_dcb_info *dcb_info, struct rte_tel_data *d) { struct rte_tel_data *tcds[RTE_ETH_DCB_NUM_TCS] = {NULL}; struct rte_eth_dcb_tc_queue_mapping *tcq; char bw_percent[RTE_TEL_MAX_STRING_LEN]; char name[RTE_TEL_MAX_STRING_LEN]; struct rte_tel_data *tcd; uint32_t i; for (i = 0; i < dcb_info->nb_tcs; i++) { tcd = rte_tel_data_alloc(); if (tcd == NULL) { while (i-- > 0) rte_tel_data_free(tcds[i]); return -ENOMEM; } tcds[i] = tcd; rte_tel_data_start_dict(tcd); rte_tel_data_add_dict_uint(tcd, "priority", dcb_info->prio_tc[i]); snprintf(bw_percent, RTE_TEL_MAX_STRING_LEN, "%u%%", dcb_info->tc_bws[i]); rte_tel_data_add_dict_string(tcd, "bw_percent", bw_percent); tcq = &dcb_info->tc_queue; rte_tel_data_add_dict_uint(tcd, "rxq_base", tcq->tc_rxq[0][i].base); rte_tel_data_add_dict_uint(tcd, "txq_base", tcq->tc_txq[0][i].base); rte_tel_data_add_dict_uint(tcd, "nb_rxq", tcq->tc_rxq[0][i].nb_queue); rte_tel_data_add_dict_uint(tcd, "nb_txq", tcq->tc_txq[0][i].nb_queue); snprintf(name, RTE_TEL_MAX_STRING_LEN, "tc%u", i); rte_tel_data_add_dict_container(d, name, tcd, 0); } return 0; } static int eth_dev_add_dcb_info(uint16_t port_id, struct rte_tel_data *d) { struct rte_eth_dcb_info dcb_info; int ret; ret = rte_eth_dev_get_dcb_info(port_id, &dcb_info); if (ret != 0) { RTE_ETHDEV_LOG_LINE(ERR, "Failed to get dcb info, ret = %d", ret); return ret; } rte_tel_data_start_dict(d); rte_tel_data_add_dict_uint(d, "tc_num", dcb_info.nb_tcs); if (dcb_info.nb_tcs > 0) return eth_dev_add_dcb_tc(&dcb_info, d); return 0; } static int eth_dev_handle_port_dcb(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { uint16_t port_id; char *end_param; int ret; ret = eth_dev_parse_port_params(params, &port_id, &end_param, false); if (ret < 0) return ret; return eth_dev_add_dcb_info(port_id, d); } static int eth_dev_add_rss_info(struct rte_eth_rss_conf *rss_conf, struct rte_tel_data *d) { const uint32_t key_len = rss_conf->rss_key_len * 2 + 1; char *rss_key; char key[3]; /* FF\0 */ uint32_t i; int ret; rss_key = malloc(key_len); if (rss_key == NULL) return -ENOMEM; rte_tel_data_start_dict(d); rte_tel_data_add_dict_uint_hex(d, "rss_hf", rss_conf->rss_hf, 0); rte_tel_data_add_dict_uint(d, "rss_key_len", rss_conf->rss_key_len); memset(rss_key, 0, key_len); for (i = 0; i < rss_conf->rss_key_len; i++) { ret = snprintf(key, 3, "%02x", rss_conf->rss_key[i]); if (ret < 0) goto free_rss_key; strlcat(rss_key, key, key_len); } ret = rte_tel_data_add_dict_string(d, "rss_key", rss_key); free_rss_key: free(rss_key); return ret; } static int eth_dev_handle_port_rss_info(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { struct rte_eth_dev_info dev_info; struct rte_eth_rss_conf rss_conf; uint8_t key_len; uint16_t port_id; char *end_param; int ret; ret = eth_dev_parse_port_params(params, &port_id, &end_param, false); if (ret < 0) return ret; ret = rte_eth_dev_info_get(port_id, &dev_info); if (ret != 0) { RTE_ETHDEV_LOG_LINE(ERR, "Failed to get device info, ret = %d", ret); return ret; } key_len = dev_info.hash_key_size ? dev_info.hash_key_size : 40; rss_conf.rss_key_len = key_len; rss_conf.rss_key = malloc(key_len); if (rss_conf.rss_key == NULL) return -ENOMEM; ret = rte_eth_dev_rss_hash_conf_get(port_id, &rss_conf); if (ret != 0) { free(rss_conf.rss_key); return ret; } ret = eth_dev_add_rss_info(&rss_conf, d); free(rss_conf.rss_key); return ret; } static const char * eth_dev_fec_capa_to_string(uint32_t fec_capa) { uint32_t i; for (i = 0; i < RTE_DIM(rte_eth_fec_capa_name); i++) { if ((fec_capa & rte_eth_fec_capa_name[i].capa) != 0) return rte_eth_fec_capa_name[i].name; } return "unknown"; } static void eth_dev_fec_capas_to_string(uint32_t fec_capa, char *fec_name, uint32_t len) { bool valid = false; size_t count = 0; uint32_t i; for (i = 0; i < RTE_DIM(rte_eth_fec_capa_name); i++) { if ((fec_capa & rte_eth_fec_capa_name[i].capa) != 0) { strlcat(fec_name, rte_eth_fec_capa_name[i].name, len); count = strlcat(fec_name, " ", len); valid = true; } } if (!valid) count = snprintf(fec_name, len, "unknown "); if (count >= len) { RTE_ETHDEV_LOG_LINE(WARNING, "FEC capa names may be truncated"); count = len; } fec_name[count - 1] = '\0'; } static int eth_dev_get_fec_capability(uint16_t port_id, struct rte_tel_data *d) { struct rte_eth_fec_capa *speed_fec_capa; char fec_name[RTE_TEL_MAX_STRING_LEN]; char speed[RTE_TEL_MAX_STRING_LEN]; uint32_t capa_num; uint32_t i, j; int ret; ret = rte_eth_fec_get_capability(port_id, NULL, 0); if (ret <= 0) return ret == 0 ? -EINVAL : ret; capa_num = ret; speed_fec_capa = calloc(capa_num, sizeof(struct rte_eth_fec_capa)); if (speed_fec_capa == NULL) return -ENOMEM; ret = rte_eth_fec_get_capability(port_id, speed_fec_capa, capa_num); if (ret <= 0) { ret = ret == 0 ? -EINVAL : ret; goto out; } for (i = 0; i < capa_num; i++) { memset(fec_name, 0, RTE_TEL_MAX_STRING_LEN); eth_dev_fec_capas_to_string(speed_fec_capa[i].capa, fec_name, RTE_TEL_MAX_STRING_LEN); memset(speed, 0, RTE_TEL_MAX_STRING_LEN); ret = snprintf(speed, RTE_TEL_MAX_STRING_LEN, "%s", rte_eth_link_speed_to_str(speed_fec_capa[i].speed)); if (ret < 0) goto out; for (j = 0; j < strlen(speed); j++) { if (speed[j] == ' ') speed[j] = '_'; } rte_tel_data_add_dict_string(d, speed, fec_name); } out: free(speed_fec_capa); return ret > 0 ? 0 : ret; } static int eth_dev_handle_port_fec(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { struct rte_tel_data *fec_capas; uint32_t fec_mode; uint16_t port_id; char *end_param; int ret; ret = eth_dev_parse_port_params(params, &port_id, &end_param, false); if (ret < 0) return ret; ret = rte_eth_fec_get(port_id, &fec_mode); if (ret != 0) return ret; rte_tel_data_start_dict(d); rte_tel_data_add_dict_string(d, "fec_mode", eth_dev_fec_capa_to_string(fec_mode)); fec_capas = rte_tel_data_alloc(); if (fec_capas == NULL) return -ENOMEM; rte_tel_data_start_dict(fec_capas); ret = eth_dev_get_fec_capability(port_id, fec_capas); if (ret != 0) { rte_tel_data_free(fec_capas); return ret; } rte_tel_data_add_dict_container(d, "fec_capability", fec_capas, 0); return 0; } static int eth_dev_add_vlan_id(int port_id, struct rte_tel_data *d) { struct rte_tel_data *vlan_blks[64] = {NULL}; uint16_t vlan_num, vidx, vbit, num_blks; char blk_name[RTE_TEL_MAX_STRING_LEN]; struct rte_vlan_filter_conf *vfc; struct rte_tel_data *vlan_blk; struct rte_tel_data *vd; uint64_t bit_width; uint64_t vlan_id; vd = rte_tel_data_alloc(); if (vd == NULL) return -ENOMEM; vfc = &rte_eth_devices[port_id].data->vlan_filter_conf; bit_width = CHAR_BIT * sizeof(uint64_t); vlan_num = 0; num_blks = 0; rte_tel_data_start_dict(vd); for (vidx = 0; vidx < RTE_DIM(vfc->ids); vidx++) { if (vfc->ids[vidx] == 0) continue; vlan_blk = rte_tel_data_alloc(); if (vlan_blk == NULL) goto free_all; vlan_blks[num_blks] = vlan_blk; num_blks++; snprintf(blk_name, RTE_TEL_MAX_STRING_LEN, "vlan_%"PRIu64"_to_%"PRIu64"", bit_width * vidx, bit_width * (vidx + 1) - 1); rte_tel_data_start_array(vlan_blk, RTE_TEL_UINT_VAL); rte_tel_data_add_dict_container(vd, blk_name, vlan_blk, 0); for (vbit = 0; vbit < bit_width; vbit++) { if ((vfc->ids[vidx] & RTE_BIT64(vbit)) == 0) continue; vlan_id = bit_width * vidx + vbit; rte_tel_data_add_array_uint(vlan_blk, vlan_id); vlan_num++; } } rte_tel_data_add_dict_uint(d, "vlan_num", vlan_num); rte_tel_data_add_dict_container(d, "vlan_ids", vd, 0); return 0; free_all: while (num_blks-- > 0) rte_tel_data_free(vlan_blks[num_blks]); rte_tel_data_free(vd); return -ENOMEM; } static int eth_dev_handle_port_vlan(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { struct rte_eth_txmode *txmode; struct rte_eth_conf dev_conf; uint16_t port_id; int offload, ret; char *end_param; ret = eth_dev_parse_port_params(params, &port_id, &end_param, false); if (ret < 0) return ret; ret = rte_eth_dev_conf_get(port_id, &dev_conf); if (ret != 0) { RTE_ETHDEV_LOG_LINE(ERR, "Failed to get device configuration, ret = %d", ret); return ret; } txmode = &dev_conf.txmode; rte_tel_data_start_dict(d); rte_tel_data_add_dict_uint(d, "pvid", txmode->pvid); rte_tel_data_add_dict_uint(d, "hw_vlan_reject_tagged", txmode->hw_vlan_reject_tagged); rte_tel_data_add_dict_uint(d, "hw_vlan_reject_untagged", txmode->hw_vlan_reject_untagged); rte_tel_data_add_dict_uint(d, "hw_vlan_insert_pvid", txmode->hw_vlan_insert_pvid); offload = rte_eth_dev_get_vlan_offload(port_id); rte_tel_data_add_dict_string(d, "VLAN_STRIP", ((offload & RTE_ETH_VLAN_STRIP_OFFLOAD) != 0) ? "on" : "off"); rte_tel_data_add_dict_string(d, "VLAN_EXTEND", ((offload & RTE_ETH_VLAN_EXTEND_OFFLOAD) != 0) ? "on" : "off"); rte_tel_data_add_dict_string(d, "QINQ_STRIP", ((offload & RTE_ETH_QINQ_STRIP_OFFLOAD) != 0) ? "on" : "off"); rte_tel_data_add_dict_string(d, "VLAN_FILTER", ((offload & RTE_ETH_VLAN_FILTER_OFFLOAD) != 0) ? "on" : "off"); return eth_dev_add_vlan_id(port_id, d); } static void eth_dev_add_tm_caps(struct rte_tel_data *d, struct rte_tm_capabilities *cap) { rte_tel_data_add_dict_uint(d, "n_nodes_max", cap->n_nodes_max); rte_tel_data_add_dict_uint(d, "n_levels_max", cap->n_levels_max); rte_tel_data_add_dict_int(d, "non_leaf_nodes_identical", cap->non_leaf_nodes_identical); rte_tel_data_add_dict_int(d, "leaf_nodes_identical", cap->leaf_nodes_identical); rte_tel_data_add_dict_uint(d, "shaper_n_max", cap->shaper_n_max); rte_tel_data_add_dict_uint(d, "shaper_private_n_max", cap->shaper_private_n_max); rte_tel_data_add_dict_int(d, "shaper_private_dual_rate_n_max", cap->shaper_private_dual_rate_n_max); rte_tel_data_add_dict_uint(d, "shaper_private_rate_min", cap->shaper_private_rate_min); rte_tel_data_add_dict_uint(d, "shaper_private_rate_max", cap->shaper_private_rate_max); rte_tel_data_add_dict_int(d, "shaper_private_packet_mode_supported", cap->shaper_private_packet_mode_supported); rte_tel_data_add_dict_int(d, "shaper_private_byte_mode_supported", cap->shaper_private_byte_mode_supported); rte_tel_data_add_dict_uint(d, "shaper_shared_n_max", cap->shaper_shared_n_max); rte_tel_data_add_dict_uint(d, "shaper_shared_n_nodes_per_shaper_max", cap->shaper_shared_n_nodes_per_shaper_max); rte_tel_data_add_dict_uint(d, "shaper_shared_n_shapers_per_node_max", cap->shaper_shared_n_shapers_per_node_max); rte_tel_data_add_dict_uint(d, "shaper_share_dual_rate_n_max", cap->shaper_shared_dual_rate_n_max); rte_tel_data_add_dict_uint(d, "shaper_shared_rate_min", cap->shaper_shared_rate_min); rte_tel_data_add_dict_uint(d, "shaper_shared_rate_max", cap->shaper_shared_rate_max); rte_tel_data_add_dict_int(d, "shaper_shared_packet_mode_supported", cap->shaper_shared_packet_mode_supported); rte_tel_data_add_dict_int(d, "shaper_shared_byte_mode_supported", cap->shaper_shared_byte_mode_supported); rte_tel_data_add_dict_int(d, "shaper_pkt_length_adjust_min", cap->shaper_pkt_length_adjust_min); rte_tel_data_add_dict_int(d, "shaper_pkt_length_adjust_max", cap->shaper_pkt_length_adjust_max); rte_tel_data_add_dict_uint(d, "sched_n_children_max", cap->sched_n_children_max); rte_tel_data_add_dict_uint(d, "sched_sp_n_priorities_max", cap->sched_sp_n_priorities_max); rte_tel_data_add_dict_uint(d, "sched_wfq_n_children_per_group_max", cap->sched_wfq_n_children_per_group_max); rte_tel_data_add_dict_uint(d, "sched_wfq_n_groups_max", cap->sched_wfq_n_groups_max); rte_tel_data_add_dict_uint(d, "sched_wfq_weight_max", cap->sched_wfq_weight_max); rte_tel_data_add_dict_int(d, "sched_wfq_packet_mode_supported", cap->sched_wfq_packet_mode_supported); rte_tel_data_add_dict_int(d, "sched_wfq_byte_mode_supported", cap->sched_wfq_byte_mode_supported); rte_tel_data_add_dict_int(d, "cman_wred_packet_mode_supported", cap->cman_wred_packet_mode_supported); rte_tel_data_add_dict_int(d, "cman_wred_byte_mode_supported", cap->cman_wred_byte_mode_supported); rte_tel_data_add_dict_int(d, "cman_head_drop_supported", cap->cman_head_drop_supported); rte_tel_data_add_dict_uint(d, "cman_wred_context_n_max", cap->cman_wred_context_n_max); rte_tel_data_add_dict_uint(d, "cman_wred_context_private_n_max", cap->cman_wred_context_private_n_max); rte_tel_data_add_dict_uint(d, "cman_wred_context_shared_n_max", cap->cman_wred_context_shared_n_max); rte_tel_data_add_dict_uint(d, "cman_wred_context_shared_n_nodes_per_context_max", cap->cman_wred_context_shared_n_nodes_per_context_max); rte_tel_data_add_dict_uint(d, "cman_wred_context_shared_n_contexts_per_node_max", cap->cman_wred_context_shared_n_contexts_per_node_max); rte_tel_data_add_dict_uint_hex(d, "dynamic_update", cap->dynamic_update_mask, 0); rte_tel_data_add_dict_uint_hex(d, "stats_mask", cap->stats_mask, 0); } static int eth_dev_handle_port_tm_caps(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { struct rte_tm_capabilities cap = {0}; struct rte_tm_error error = {0}; uint16_t port_id; char *end_param; int ret; ret = eth_dev_parse_port_params(params, &port_id, &end_param, false); if (ret != 0) return ret; ret = rte_tm_capabilities_get(port_id, &cap, &error); if (ret != 0) { RTE_ETHDEV_LOG_LINE(ERR, "error: %s, error type: %u", error.message ? error.message : "no stated reason", error.type); return ret; } rte_tel_data_start_dict(d); eth_dev_add_tm_caps(d, &cap); return 0; } static void eth_dev_add_tm_level_basic_caps(struct rte_tel_data *d, struct rte_tm_level_capabilities *cap) { rte_tel_data_add_dict_uint(d, "n_nodes_max", cap->n_nodes_max); rte_tel_data_add_dict_uint(d, "n_nodes_nonleaf_max", cap->n_nodes_nonleaf_max); rte_tel_data_add_dict_uint(d, "n_nodes_leaf_max", cap->n_nodes_leaf_max); rte_tel_data_add_dict_int(d, "non_leaf_nodes_identical", cap->non_leaf_nodes_identical); rte_tel_data_add_dict_int(d, "leaf_nodes_identical", cap->leaf_nodes_identical); } static void eth_dev_add_tm_level_nonleaf_caps(struct rte_tel_data *nonleaf, struct rte_tm_level_capabilities *cap) { rte_tel_data_add_dict_int(nonleaf, "shaper_private_supported", cap->nonleaf.shaper_private_supported); rte_tel_data_add_dict_int(nonleaf, "shaper_private_dual_rate_supported", cap->nonleaf.shaper_private_dual_rate_supported); rte_tel_data_add_dict_uint(nonleaf, "shaper_private_rate_min", cap->nonleaf.shaper_private_rate_min); rte_tel_data_add_dict_uint(nonleaf, "shaper_private_rate_max", cap->nonleaf.shaper_private_rate_max); rte_tel_data_add_dict_int(nonleaf, "shaper_private_packet_mode_supported", cap->nonleaf.shaper_private_packet_mode_supported); rte_tel_data_add_dict_int(nonleaf, "shaper_private_byte_mode_supported", cap->nonleaf.shaper_private_byte_mode_supported); rte_tel_data_add_dict_uint(nonleaf, "shaper_shared_n_max", cap->nonleaf.shaper_shared_n_max); rte_tel_data_add_dict_int(nonleaf, "shaper_shared_packet_mode_supported", cap->nonleaf.shaper_shared_packet_mode_supported); rte_tel_data_add_dict_int(nonleaf, "shaper_shared_byte_mode_supported", cap->nonleaf.shaper_shared_byte_mode_supported); rte_tel_data_add_dict_uint(nonleaf, "sched_n_children_max", cap->nonleaf.sched_n_children_max); rte_tel_data_add_dict_uint(nonleaf, "sched_sp_n_priorities_max", cap->nonleaf.sched_sp_n_priorities_max); rte_tel_data_add_dict_uint(nonleaf, "sched_wfq_n_children_per_group_max", cap->nonleaf.sched_wfq_n_children_per_group_max); rte_tel_data_add_dict_uint(nonleaf, "sched_wfq_n_groups_max", cap->nonleaf.sched_wfq_n_groups_max); rte_tel_data_add_dict_uint(nonleaf, "sched_wfq_weight_max", cap->nonleaf.sched_wfq_weight_max); rte_tel_data_add_dict_int(nonleaf, "sched_wfq_packet_mode_supported", cap->nonleaf.sched_wfq_packet_mode_supported); rte_tel_data_add_dict_int(nonleaf, "sched_wfq_byte_mode_supported", cap->nonleaf.sched_wfq_byte_mode_supported); rte_tel_data_add_dict_uint_hex(nonleaf, "stats_mask", cap->nonleaf.stats_mask, 0); } static void eth_dev_add_tm_level_leaf_caps(struct rte_tel_data *leaf, struct rte_tm_level_capabilities *cap) { rte_tel_data_add_dict_int(leaf, "shaper_private_supported", cap->leaf.shaper_private_supported); rte_tel_data_add_dict_int(leaf, "shaper_private_dual_rate_supported", cap->leaf.shaper_private_dual_rate_supported); rte_tel_data_add_dict_uint(leaf, "shaper_private_rate_min", cap->leaf.shaper_private_rate_min); rte_tel_data_add_dict_uint(leaf, "shaper_private_rate_max", cap->leaf.shaper_private_rate_max); rte_tel_data_add_dict_int(leaf, "shaper_private_packet_mode_supported", cap->leaf.shaper_private_packet_mode_supported); rte_tel_data_add_dict_int(leaf, "shaper_private_byte_mode_supported", cap->leaf.shaper_private_byte_mode_supported); rte_tel_data_add_dict_uint(leaf, "shaper_shared_n_max", cap->leaf.shaper_shared_n_max); rte_tel_data_add_dict_int(leaf, "shaper_shared_packet_mode_supported", cap->leaf.shaper_shared_packet_mode_supported); rte_tel_data_add_dict_int(leaf, "shaper_shared_byte_mode_supported", cap->leaf.shaper_shared_byte_mode_supported); rte_tel_data_add_dict_int(leaf, "cman_wred_packet_mode_supported", cap->leaf.cman_wred_packet_mode_supported); rte_tel_data_add_dict_int(leaf, "cman_wred_byte_mode_supported", cap->leaf.cman_wred_byte_mode_supported); rte_tel_data_add_dict_int(leaf, "cman_head_drop_supported", cap->leaf.cman_head_drop_supported); rte_tel_data_add_dict_int(leaf, "cman_wred_context_private_supported", cap->leaf.cman_wred_context_private_supported); rte_tel_data_add_dict_uint(leaf, "cman_wred_context_shared_n_max", cap->leaf.cman_wred_context_shared_n_max); rte_tel_data_add_dict_uint_hex(leaf, "stats_mask", cap->leaf.stats_mask, 0); } static int eth_dev_parse_tm_params(char *params, uint32_t *result) { const char *splited_param; uint64_t ret; splited_param = strtok(params, ","); if (!splited_param || strlen(splited_param) == 0 || !isdigit(*splited_param)) return -EINVAL; ret = strtoul(splited_param, ¶ms, 0); if (*params != '\0') RTE_ETHDEV_LOG_LINE(NOTICE, "Extra parameters passed to ethdev telemetry command, ignoring"); if (ret >= UINT32_MAX) return -EINVAL; *result = ret; return 0; } static int eth_dev_handle_port_tm_level_caps(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { struct rte_tm_level_capabilities cap = {0}; struct rte_tm_error error = {0}; struct rte_tel_data *nonleaf; struct rte_tel_data *leaf; uint32_t level_id; uint16_t port_id; char *end_param; int ret; ret = eth_dev_parse_port_params(params, &port_id, &end_param, true); if (ret != 0) return ret; ret = eth_dev_parse_tm_params(end_param, &level_id); if (ret != 0) return ret; ret = rte_tm_level_capabilities_get(port_id, level_id, &cap, &error); if (ret != 0) { RTE_ETHDEV_LOG_LINE(ERR, "error: %s, error type: %u", error.message ? error.message : "no stated reason", error.type); return ret; } rte_tel_data_start_dict(d); eth_dev_add_tm_level_basic_caps(d, &cap); nonleaf = rte_tel_data_alloc(); if (nonleaf == NULL) return -ENOMEM; rte_tel_data_start_dict(nonleaf); eth_dev_add_tm_level_nonleaf_caps(nonleaf, &cap); rte_tel_data_add_dict_container(d, "nonleaf_cap", nonleaf, 0); leaf = rte_tel_data_alloc(); if (leaf == NULL) { rte_tel_data_free(nonleaf); return -ENOMEM; } rte_tel_data_start_dict(leaf); eth_dev_add_tm_level_leaf_caps(leaf, &cap); rte_tel_data_add_dict_container(d, "leaf_cap", leaf, 0); return 0; } static void eth_dev_add_tm_node_basic_caps(struct rte_tel_data *node_data, struct rte_tm_node_capabilities *capnode) { rte_tel_data_add_dict_int(node_data, "shaper_private_supported", capnode->shaper_private_supported); rte_tel_data_add_dict_int(node_data, "shaper_private_dual_rate_supported", capnode->shaper_private_dual_rate_supported); rte_tel_data_add_dict_uint(node_data, "shaper_private_rate_min", capnode->shaper_private_rate_min); rte_tel_data_add_dict_uint(node_data, "shaper_private_rate_max", capnode->shaper_private_rate_max); rte_tel_data_add_dict_int(node_data, "shaper_private_packet_mode_supported", capnode->shaper_private_packet_mode_supported); rte_tel_data_add_dict_int(node_data, "shaper_private_byte_mode_supported", capnode->shaper_private_byte_mode_supported); rte_tel_data_add_dict_uint(node_data, "shaper_shared_n_max", capnode->shaper_shared_n_max); rte_tel_data_add_dict_int(node_data, "shaper_shared_packet_mode_supported", capnode->shaper_shared_packet_mode_supported); rte_tel_data_add_dict_int(node_data, "shaper_shared_byte_mode_supported", capnode->shaper_shared_byte_mode_supported); rte_tel_data_add_dict_uint_hex(node_data, "stats_mask", capnode->stats_mask, 0); } static void eth_dev_add_tm_type_node_caps(struct rte_tel_data *d, int is_leaf, struct rte_tm_node_capabilities *cap) { rte_tel_data_add_dict_string(d, "node_type", is_leaf == 0 ? "nonleaf" : "leaf"); if (is_leaf == 0) { rte_tel_data_add_dict_uint(d, "children_max", cap->nonleaf.sched_n_children_max); rte_tel_data_add_dict_uint(d, "priorities_max", cap->nonleaf.sched_sp_n_priorities_max); rte_tel_data_add_dict_uint(d, "sched_wfq_n_children_per_group_max", cap->nonleaf.sched_wfq_n_children_per_group_max); rte_tel_data_add_dict_uint(d, "sched_wfq_n_groups_max", cap->nonleaf.sched_wfq_n_groups_max); rte_tel_data_add_dict_uint(d, "sched_wfq_weight_max", cap->nonleaf.sched_wfq_weight_max); rte_tel_data_add_dict_int(d, "sched_wfq_packet_mode_supported", cap->nonleaf.sched_wfq_packet_mode_supported); rte_tel_data_add_dict_int(d, "sched_wfq_byte_mode_supported", cap->nonleaf.sched_wfq_byte_mode_supported); } else { rte_tel_data_add_dict_int(d, "cman_wred_packet_mode_supported", cap->leaf.cman_wred_packet_mode_supported); rte_tel_data_add_dict_int(d, "cman_wred_byte_mode_supported", cap->leaf.cman_wred_byte_mode_supported); rte_tel_data_add_dict_int(d, "cman_head_drop_supported", cap->leaf.cman_head_drop_supported); rte_tel_data_add_dict_int(d, "cman_wred_context_private_supported", cap->leaf.cman_wred_context_private_supported); rte_tel_data_add_dict_uint(d, "cman_wred_context_shared_n_max", cap->leaf.cman_wred_context_shared_n_max); } } static int eth_dev_handle_port_tm_node_caps(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { struct rte_tm_node_capabilities cap = {0}; struct rte_tm_error error = {0}; uint32_t node_id; uint16_t port_id; char *end_param; int is_leaf; int ret; ret = eth_dev_parse_port_params(params, &port_id, &end_param, true); if (ret != 0) return ret; ret = eth_dev_parse_tm_params(end_param, &node_id); if (ret != 0) return ret; ret = rte_tm_node_capabilities_get(port_id, node_id, &cap, &error); if (ret != 0) goto out; ret = rte_tm_node_type_get(port_id, node_id, &is_leaf, &error); if (ret != 0) goto out; rte_tel_data_start_dict(d); eth_dev_add_tm_node_basic_caps(d, &cap); eth_dev_add_tm_type_node_caps(d, is_leaf, &cap); return 0; out: RTE_ETHDEV_LOG_LINE(WARNING, "error: %s, error type: %u", error.message ? error.message : "no stated reason", error.type); return ret; } static void eth_dev_add_reg_data(struct rte_tel_data *d, struct rte_dev_reg_info *reg_info, uint32_t idx) { if (reg_info->width == sizeof(uint32_t)) rte_tel_data_add_dict_uint_hex(d, reg_info->names[idx].name, *((uint32_t *)reg_info->data + idx), 0); else rte_tel_data_add_dict_uint_hex(d, reg_info->names[idx].name, *((uint64_t *)reg_info->data + idx), 0); } static int eth_dev_store_regs(struct rte_tel_data *d, struct rte_dev_reg_info *reg_info) { struct rte_tel_data *groups[RTE_TEL_MAX_DICT_ENTRIES]; char group_name[RTE_TEL_MAX_STRING_LEN] = {0}; struct rte_tel_data *group = NULL; uint32_t grp_num = 0; uint32_t i, max_cap; int ret; rte_tel_data_start_dict(d); rte_tel_data_add_dict_uint(d, "register_length", reg_info->length); rte_tel_data_add_dict_uint(d, "register_width", reg_info->width); rte_tel_data_add_dict_uint_hex(d, "register_offset", reg_info->offset, 0); rte_tel_data_add_dict_uint_hex(d, "version", reg_info->version, 0); max_cap = (RTE_TEL_MAX_DICT_ENTRIES - 4) * RTE_TEL_MAX_DICT_ENTRIES; if (reg_info->length > max_cap) { RTE_ETHDEV_LOG_LINE(WARNING, "Registers to be displayed are reduced from %u to %u due to limited capacity", reg_info->length, max_cap); reg_info->length = max_cap; } for (i = 0; i < reg_info->length; i++) { if (i % RTE_TEL_MAX_DICT_ENTRIES != 0) { eth_dev_add_reg_data(group, reg_info, i); continue; } group = rte_tel_data_alloc(); if (group == NULL) { ret = -ENOMEM; RTE_ETHDEV_LOG_LINE(WARNING, "No enough memory for group data"); goto out; } groups[grp_num++] = group; rte_tel_data_start_dict(group); eth_dev_add_reg_data(group, reg_info, i); } for (i = 0; i < grp_num; i++) { snprintf(group_name, RTE_TEL_MAX_STRING_LEN, "group_%u", i); rte_tel_data_add_dict_container(d, group_name, groups[i], 0); } return 0; out: for (i = 0; i < grp_num; i++) rte_tel_data_free(groups[i]); return ret; } static int eth_dev_get_port_regs(int port_id, struct rte_tel_data *d, char *filter) { struct rte_dev_reg_info reg_info; int ret; memset(®_info, 0, sizeof(reg_info)); reg_info.filter = filter; ret = rte_eth_dev_get_reg_info_ext(port_id, ®_info); if (ret != 0) { RTE_ETHDEV_LOG_LINE(ERR, "Failed to get device reg info: %d", ret); return ret; } reg_info.data = calloc(reg_info.length, reg_info.width); if (reg_info.data == NULL) { RTE_ETHDEV_LOG_LINE(ERR, "Failed to allocate memory for reg_info.data"); return -ENOMEM; } reg_info.names = calloc(reg_info.length, sizeof(struct rte_eth_reg_name)); if (reg_info.names == NULL) { RTE_ETHDEV_LOG_LINE(ERR, "Failed to allocate memory for reg_info.names"); free(reg_info.data); return -ENOMEM; } ret = rte_eth_dev_get_reg_info_ext(port_id, ®_info); if (ret != 0) { RTE_ETHDEV_LOG_LINE(ERR, "Failed to get device reg info: %d", ret); ret = -EINVAL; goto out; } ret = eth_dev_store_regs(d, ®_info); out: free(reg_info.data); free(reg_info.names); return ret; } static int eth_dev_handle_port_regs(const char *cmd __rte_unused, const char *params, struct rte_tel_data *d) { char *filter, *end_param; uint16_t port_id; int ret; ret = eth_dev_parse_port_params(params, &port_id, &end_param, true); if (ret != 0) return ret; filter = strtok(end_param, ","); if (filter != NULL && strlen(filter) == 0) filter = NULL; return eth_dev_get_port_regs(port_id, d, filter); } static int eth_dev_telemetry_do(const char *cmd, const char *params, void *arg, struct rte_tel_data *d) { telemetry_cb fn = arg; int ret; /* Protect against port removal while invoking callback, calling ethdev API. */ rte_spinlock_lock(rte_mcfg_ethdev_get_lock()); ret = fn(cmd, params, d); rte_spinlock_unlock(rte_mcfg_ethdev_get_lock()); return ret; } RTE_INIT(ethdev_init_telemetry) { rte_telemetry_register_cmd_arg("/ethdev/list", eth_dev_telemetry_do, eth_dev_handle_port_list, "Returns list of available ethdev ports. Takes no parameters"); rte_telemetry_register_cmd_arg("/ethdev/stats", eth_dev_telemetry_do, eth_dev_handle_port_stats, "Returns the common stats for a port. Parameters: int port_id"); rte_telemetry_register_cmd_arg("/ethdev/xstats", eth_dev_telemetry_do, eth_dev_handle_port_xstats, "Returns the extended stats for a port. Parameters: int port_id,hide_zero=true|false(Optional for indicates hide zero xstats)"); #ifndef RTE_EXEC_ENV_WINDOWS rte_telemetry_register_cmd_arg("/ethdev/dump_priv", eth_dev_telemetry_do, eth_dev_handle_port_dump_priv, "Returns dump private information for a port. Parameters: int port_id"); #endif rte_telemetry_register_cmd_arg("/ethdev/link_status", eth_dev_telemetry_do, eth_dev_handle_port_link_status, "Returns the link status for a port. Parameters: int port_id"); rte_telemetry_register_cmd_arg("/ethdev/info", eth_dev_telemetry_do, eth_dev_handle_port_info, "Returns the device info for a port. Parameters: int port_id"); rte_telemetry_register_cmd_arg("/ethdev/module_eeprom", eth_dev_telemetry_do, eth_dev_handle_port_module_eeprom, "Returns module EEPROM info with SFF specs. Parameters: int port_id"); rte_telemetry_register_cmd_arg("/ethdev/macs", eth_dev_telemetry_do, eth_dev_handle_port_macs, "Returns the MAC addresses for a port. Parameters: int port_id"); rte_telemetry_register_cmd_arg("/ethdev/flow_ctrl", eth_dev_telemetry_do, eth_dev_handle_port_flow_ctrl, "Returns flow ctrl info for a port. Parameters: int port_id"); rte_telemetry_register_cmd_arg("/ethdev/rx_queue", eth_dev_telemetry_do, eth_dev_handle_port_rxq, "Returns Rx queue info for a port. Parameters: int port_id, int queue_id (Optional if only one queue)"); rte_telemetry_register_cmd_arg("/ethdev/tx_queue", eth_dev_telemetry_do, eth_dev_handle_port_txq, "Returns Tx queue info for a port. Parameters: int port_id, int queue_id (Optional if only one queue)"); rte_telemetry_register_cmd_arg("/ethdev/dcb", eth_dev_telemetry_do, eth_dev_handle_port_dcb, "Returns DCB info for a port. Parameters: int port_id"); rte_telemetry_register_cmd_arg("/ethdev/rss_info", eth_dev_telemetry_do, eth_dev_handle_port_rss_info, "Returns RSS info for a port. Parameters: int port_id"); rte_telemetry_register_cmd_arg("/ethdev/fec", eth_dev_telemetry_do, eth_dev_handle_port_fec, "Returns FEC info for a port. Parameters: int port_id"); rte_telemetry_register_cmd_arg("/ethdev/vlan", eth_dev_telemetry_do, eth_dev_handle_port_vlan, "Returns VLAN info for a port. Parameters: int port_id"); rte_telemetry_register_cmd_arg("/ethdev/tm_capability", eth_dev_telemetry_do, eth_dev_handle_port_tm_caps, "Returns TM Capabilities info for a port. Parameters: int port_id"); rte_telemetry_register_cmd_arg("/ethdev/tm_level_capability", eth_dev_telemetry_do, eth_dev_handle_port_tm_level_caps, "Returns TM Level Capabilities info for a port. Parameters: int port_id, int level_id (see tm_capability for the max)"); rte_telemetry_register_cmd_arg("/ethdev/tm_node_capability", eth_dev_telemetry_do, eth_dev_handle_port_tm_node_caps, "Returns TM Node Capabilities info for a port. Parameters: int port_id, int node_id (see tm_capability for the max)"); rte_telemetry_register_cmd("/ethdev/regs", eth_dev_handle_port_regs, "Returns all or filtered registers info for a port. Parameters: int port_id, string module_name (Optional if show all)"); }