xref: /dpdk/lib/ethdev/rte_ethdev_telemetry.c (revision 1d343c19330a11f05e3ea369ae5780d38772358e)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2017 Intel Corporation
3  */
4 
5 #include <ctype.h>
6 #include <stdlib.h>
7 
8 #include <rte_kvargs.h>
9 #include <rte_telemetry.h>
10 
11 #include "rte_ethdev.h"
12 #include "ethdev_driver.h"
13 #include "sff_telemetry.h"
14 
15 static const struct {
16 	uint32_t capa;
17 	const char *name;
18 } rte_eth_fec_capa_name[] = {
19 	{ RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC),	"off"	},
20 	{ RTE_ETH_FEC_MODE_CAPA_MASK(AUTO),	"auto"	},
21 	{ RTE_ETH_FEC_MODE_CAPA_MASK(BASER),	"baser"	},
22 	{ RTE_ETH_FEC_MODE_CAPA_MASK(RS),	"rs"	},
23 	{ RTE_ETH_FEC_MODE_CAPA_MASK(LLRS),	"llrs"	},
24 };
25 
26 static int
27 eth_dev_parse_port_params(const char *params, uint16_t *port_id,
28 		char **end_param, bool has_next)
29 {
30 	uint64_t pi;
31 
32 	if (params == NULL || strlen(params) == 0 ||
33 		!isdigit(*params) || port_id == NULL)
34 		return -EINVAL;
35 
36 	pi = strtoul(params, end_param, 0);
37 	if (**end_param != '\0' && !has_next)
38 		RTE_ETHDEV_LOG(NOTICE,
39 			"Extra parameters passed to ethdev telemetry command, ignoring\n");
40 
41 	if (pi >= UINT16_MAX || !rte_eth_dev_is_valid_port(pi))
42 		return -EINVAL;
43 
44 	*port_id = (uint16_t)pi;
45 
46 	return 0;
47 }
48 
49 static int
50 eth_dev_handle_port_list(const char *cmd __rte_unused,
51 		const char *params __rte_unused,
52 		struct rte_tel_data *d)
53 {
54 	int port_id;
55 
56 	rte_tel_data_start_array(d, RTE_TEL_INT_VAL);
57 	RTE_ETH_FOREACH_DEV(port_id)
58 		rte_tel_data_add_array_int(d, port_id);
59 	return 0;
60 }
61 
62 static void
63 eth_dev_add_port_queue_stats(struct rte_tel_data *d, uint64_t *q_stats,
64 		const char *stat_name)
65 {
66 	int q;
67 	struct rte_tel_data *q_data = rte_tel_data_alloc();
68 	if (q_data == NULL)
69 		return;
70 	rte_tel_data_start_array(q_data, RTE_TEL_UINT_VAL);
71 	for (q = 0; q < RTE_ETHDEV_QUEUE_STAT_CNTRS; q++)
72 		rte_tel_data_add_array_uint(q_data, q_stats[q]);
73 	rte_tel_data_add_dict_container(d, stat_name, q_data, 0);
74 }
75 
76 static int
77 eth_dev_parse_hide_zero(const char *key, const char *value, void *extra_args)
78 {
79 	RTE_SET_USED(key);
80 
81 	if (value == NULL)
82 		return -1;
83 
84 	if (strcmp(value, "true") == 0)
85 		*(bool *)extra_args = true;
86 	else if (strcmp(value, "false") == 0)
87 		*(bool *)extra_args = false;
88 	else
89 		return -1;
90 
91 	return 0;
92 }
93 
94 #define ADD_DICT_STAT(stats, s) rte_tel_data_add_dict_uint(d, #s, stats.s)
95 
96 static int
97 eth_dev_handle_port_stats(const char *cmd __rte_unused,
98 		const char *params,
99 		struct rte_tel_data *d)
100 {
101 	struct rte_eth_stats stats;
102 	uint16_t port_id;
103 	char *end_param;
104 	int ret;
105 
106 	ret = eth_dev_parse_port_params(params, &port_id, &end_param, false);
107 	if (ret < 0)
108 		return ret;
109 
110 	ret = rte_eth_stats_get(port_id, &stats);
111 	if (ret < 0)
112 		return -1;
113 
114 	rte_tel_data_start_dict(d);
115 	ADD_DICT_STAT(stats, ipackets);
116 	ADD_DICT_STAT(stats, opackets);
117 	ADD_DICT_STAT(stats, ibytes);
118 	ADD_DICT_STAT(stats, obytes);
119 	ADD_DICT_STAT(stats, imissed);
120 	ADD_DICT_STAT(stats, ierrors);
121 	ADD_DICT_STAT(stats, oerrors);
122 	ADD_DICT_STAT(stats, rx_nombuf);
123 	eth_dev_add_port_queue_stats(d, stats.q_ipackets, "q_ipackets");
124 	eth_dev_add_port_queue_stats(d, stats.q_opackets, "q_opackets");
125 	eth_dev_add_port_queue_stats(d, stats.q_ibytes, "q_ibytes");
126 	eth_dev_add_port_queue_stats(d, stats.q_obytes, "q_obytes");
127 	eth_dev_add_port_queue_stats(d, stats.q_errors, "q_errors");
128 
129 	return 0;
130 }
131 
132 static int
133 eth_dev_handle_port_xstats(const char *cmd __rte_unused,
134 		const char *params,
135 		struct rte_tel_data *d)
136 {
137 	const char *const valid_keys[] = { "hide_zero", NULL };
138 	struct rte_eth_xstat *eth_xstats;
139 	struct rte_eth_xstat_name *xstat_names;
140 	struct rte_kvargs *kvlist;
141 	bool hide_zero = false;
142 	uint16_t port_id;
143 	char *end_param;
144 	int num_xstats;
145 	int i, ret;
146 
147 	ret = eth_dev_parse_port_params(params, &port_id, &end_param, true);
148 	if (ret < 0)
149 		return ret;
150 
151 	if (*end_param != '\0') {
152 		kvlist = rte_kvargs_parse(end_param, valid_keys);
153 		ret = rte_kvargs_process(kvlist, NULL, eth_dev_parse_hide_zero, &hide_zero);
154 		if (kvlist == NULL || ret != 0)
155 			RTE_ETHDEV_LOG(NOTICE,
156 				"Unknown extra parameters passed to ethdev telemetry command, ignoring\n");
157 		rte_kvargs_free(kvlist);
158 	}
159 
160 	num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
161 	if (num_xstats < 0)
162 		return -1;
163 
164 	/* use one malloc for both names and stats */
165 	eth_xstats = malloc((sizeof(struct rte_eth_xstat) +
166 			sizeof(struct rte_eth_xstat_name)) * num_xstats);
167 	if (eth_xstats == NULL)
168 		return -1;
169 	xstat_names = (void *)&eth_xstats[num_xstats];
170 
171 	ret = rte_eth_xstats_get_names(port_id, xstat_names, num_xstats);
172 	if (ret < 0 || ret > num_xstats) {
173 		free(eth_xstats);
174 		return -1;
175 	}
176 
177 	ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
178 	if (ret < 0 || ret > num_xstats) {
179 		free(eth_xstats);
180 		return -1;
181 	}
182 
183 	rte_tel_data_start_dict(d);
184 	for (i = 0; i < num_xstats; i++) {
185 		if (hide_zero && eth_xstats[i].value == 0)
186 			continue;
187 		rte_tel_data_add_dict_uint(d, xstat_names[i].name,
188 					   eth_xstats[i].value);
189 	}
190 	free(eth_xstats);
191 	return 0;
192 }
193 
194 #ifndef RTE_EXEC_ENV_WINDOWS
195 static int
196 eth_dev_handle_port_dump_priv(const char *cmd __rte_unused,
197 			const char *params,
198 			struct rte_tel_data *d)
199 {
200 	char *buf, *end_param;
201 	uint16_t port_id;
202 	int ret;
203 	FILE *f;
204 
205 	ret = eth_dev_parse_port_params(params, &port_id, &end_param, false);
206 	if (ret < 0)
207 		return ret;
208 
209 	buf = calloc(RTE_TEL_MAX_SINGLE_STRING_LEN, sizeof(char));
210 	if (buf == NULL)
211 		return -ENOMEM;
212 
213 	f = fmemopen(buf, RTE_TEL_MAX_SINGLE_STRING_LEN - 1, "w+");
214 	if (f == NULL) {
215 		free(buf);
216 		return -EINVAL;
217 	}
218 
219 	ret = rte_eth_dev_priv_dump(port_id, f);
220 	fclose(f);
221 	if (ret == 0) {
222 		rte_tel_data_start_dict(d);
223 		rte_tel_data_string(d, buf);
224 	}
225 
226 	free(buf);
227 	return 0;
228 }
229 #endif /* !RTE_EXEC_ENV_WINDOWS */
230 
231 static int
232 eth_dev_handle_port_link_status(const char *cmd __rte_unused,
233 		const char *params,
234 		struct rte_tel_data *d)
235 {
236 	static const char *status_str = "status";
237 	struct rte_eth_link link;
238 	uint16_t port_id;
239 	char *end_param;
240 	int ret;
241 
242 	ret = eth_dev_parse_port_params(params, &port_id, &end_param, false);
243 	if (ret < 0)
244 		return ret;
245 
246 	ret = rte_eth_link_get_nowait(port_id, &link);
247 	if (ret < 0)
248 		return -1;
249 
250 	rte_tel_data_start_dict(d);
251 	if (!link.link_status) {
252 		rte_tel_data_add_dict_string(d, status_str, "DOWN");
253 		return 0;
254 	}
255 	rte_tel_data_add_dict_string(d, status_str, "UP");
256 	rte_tel_data_add_dict_uint(d, "speed", link.link_speed);
257 	rte_tel_data_add_dict_string(d, "duplex",
258 			(link.link_duplex == RTE_ETH_LINK_FULL_DUPLEX) ?
259 				"full-duplex" : "half-duplex");
260 	return 0;
261 }
262 
263 static void
264 eth_dev_parse_rx_offloads(uint64_t offload, struct rte_tel_data *d)
265 {
266 	uint64_t i;
267 
268 	rte_tel_data_start_array(d, RTE_TEL_STRING_VAL);
269 	for (i = 0; i < CHAR_BIT * sizeof(offload); i++) {
270 		if ((offload & RTE_BIT64(i)) != 0)
271 			rte_tel_data_add_array_string(d,
272 				rte_eth_dev_rx_offload_name(offload & RTE_BIT64(i)));
273 	}
274 }
275 
276 static void
277 eth_dev_parse_tx_offloads(uint64_t offload, struct rte_tel_data *d)
278 {
279 	uint64_t i;
280 
281 	rte_tel_data_start_array(d, RTE_TEL_STRING_VAL);
282 	for (i = 0; i < CHAR_BIT * sizeof(offload); i++) {
283 		if ((offload & RTE_BIT64(i)) != 0)
284 			rte_tel_data_add_array_string(d,
285 				rte_eth_dev_tx_offload_name(offload & RTE_BIT64(i)));
286 	}
287 }
288 
289 static int
290 eth_dev_handle_port_info(const char *cmd __rte_unused,
291 		const char *params,
292 		struct rte_tel_data *d)
293 {
294 	struct rte_tel_data *rx_offload, *tx_offload;
295 	struct rte_tel_data *rxq_state, *txq_state;
296 	char fw_version[RTE_TEL_MAX_STRING_LEN];
297 	char mac_addr[RTE_ETHER_ADDR_FMT_SIZE];
298 	struct rte_eth_dev *eth_dev;
299 	uint16_t port_id;
300 	char *end_param;
301 	int ret;
302 	int i;
303 
304 	ret = eth_dev_parse_port_params(params, &port_id, &end_param, false);
305 	if (ret < 0)
306 		return ret;
307 
308 	eth_dev = &rte_eth_devices[port_id];
309 
310 	rxq_state = rte_tel_data_alloc();
311 	if (rxq_state == NULL)
312 		return -ENOMEM;
313 
314 	txq_state = rte_tel_data_alloc();
315 	if (txq_state == NULL)
316 		goto free_rxq_state;
317 
318 	rx_offload = rte_tel_data_alloc();
319 	if (rx_offload == NULL)
320 		goto free_txq_state;
321 
322 	tx_offload = rte_tel_data_alloc();
323 	if (tx_offload == NULL)
324 		goto free_rx_offload;
325 
326 	rte_tel_data_start_dict(d);
327 	rte_tel_data_add_dict_string(d, "name", eth_dev->data->name);
328 
329 	if (rte_eth_dev_fw_version_get(port_id, fw_version,
330 					 RTE_TEL_MAX_STRING_LEN) == 0)
331 		rte_tel_data_add_dict_string(d, "fw_version", fw_version);
332 
333 	rte_tel_data_add_dict_int(d, "state", eth_dev->state);
334 	rte_tel_data_add_dict_int(d, "nb_rx_queues",
335 			eth_dev->data->nb_rx_queues);
336 	rte_tel_data_add_dict_int(d, "nb_tx_queues",
337 			eth_dev->data->nb_tx_queues);
338 	rte_tel_data_add_dict_int(d, "port_id", eth_dev->data->port_id);
339 	rte_tel_data_add_dict_int(d, "mtu", eth_dev->data->mtu);
340 	rte_tel_data_add_dict_uint(d, "rx_mbuf_size_min",
341 			eth_dev->data->min_rx_buf_size);
342 	rte_ether_format_addr(mac_addr, sizeof(mac_addr),
343 			eth_dev->data->mac_addrs);
344 	rte_tel_data_add_dict_string(d, "mac_addr", mac_addr);
345 	rte_tel_data_add_dict_int(d, "promiscuous",
346 			eth_dev->data->promiscuous);
347 	rte_tel_data_add_dict_int(d, "scattered_rx",
348 			eth_dev->data->scattered_rx);
349 	rte_tel_data_add_dict_int(d, "all_multicast",
350 			eth_dev->data->all_multicast);
351 	rte_tel_data_add_dict_int(d, "dev_started", eth_dev->data->dev_started);
352 	rte_tel_data_add_dict_int(d, "lro", eth_dev->data->lro);
353 	rte_tel_data_add_dict_int(d, "dev_configured",
354 			eth_dev->data->dev_configured);
355 
356 	rte_tel_data_start_array(rxq_state, RTE_TEL_INT_VAL);
357 	for (i = 0; i < eth_dev->data->nb_rx_queues; i++)
358 		rte_tel_data_add_array_int(rxq_state,
359 				eth_dev->data->rx_queue_state[i]);
360 
361 	rte_tel_data_start_array(txq_state, RTE_TEL_INT_VAL);
362 	for (i = 0; i < eth_dev->data->nb_tx_queues; i++)
363 		rte_tel_data_add_array_int(txq_state,
364 				eth_dev->data->tx_queue_state[i]);
365 
366 	rte_tel_data_add_dict_container(d, "rxq_state", rxq_state, 0);
367 	rte_tel_data_add_dict_container(d, "txq_state", txq_state, 0);
368 	rte_tel_data_add_dict_int(d, "numa_node", eth_dev->data->numa_node);
369 	rte_tel_data_add_dict_uint_hex(d, "dev_flags",
370 			eth_dev->data->dev_flags, 0);
371 
372 	eth_dev_parse_rx_offloads(eth_dev->data->dev_conf.rxmode.offloads,
373 			rx_offload);
374 	rte_tel_data_add_dict_container(d, "rx_offloads", rx_offload, 0);
375 	eth_dev_parse_tx_offloads(eth_dev->data->dev_conf.txmode.offloads,
376 			tx_offload);
377 	rte_tel_data_add_dict_container(d, "tx_offloads", tx_offload, 0);
378 
379 	rte_tel_data_add_dict_uint_hex(d, "ethdev_rss_hf",
380 			eth_dev->data->dev_conf.rx_adv_conf.rss_conf.rss_hf, 0);
381 
382 	return 0;
383 
384 free_rx_offload:
385 	rte_tel_data_free(rx_offload);
386 free_txq_state:
387 	rte_tel_data_free(txq_state);
388 free_rxq_state:
389 	rte_tel_data_free(rxq_state);
390 
391 	return -ENOMEM;
392 }
393 
394 static int
395 eth_dev_handle_port_macs(const char *cmd __rte_unused,
396 		const char *params,
397 		struct rte_tel_data *d)
398 {
399 	char mac_addr[RTE_ETHER_ADDR_FMT_SIZE];
400 	struct rte_eth_dev_info dev_info;
401 	struct rte_eth_dev *eth_dev;
402 	uint16_t port_id;
403 	char *end_param;
404 	uint32_t i;
405 	int ret;
406 
407 	ret = eth_dev_parse_port_params(params, &port_id, &end_param, false);
408 	if (ret < 0)
409 		return ret;
410 
411 	ret = rte_eth_dev_info_get(port_id, &dev_info);
412 	if (ret != 0)
413 		return ret;
414 
415 	eth_dev = &rte_eth_devices[port_id];
416 	rte_tel_data_start_array(d, RTE_TEL_STRING_VAL);
417 	for (i = 0; i < dev_info.max_mac_addrs; i++) {
418 		if (rte_is_zero_ether_addr(&eth_dev->data->mac_addrs[i]))
419 			continue;
420 
421 		rte_ether_format_addr(mac_addr, sizeof(mac_addr),
422 			&eth_dev->data->mac_addrs[i]);
423 		rte_tel_data_add_array_string(d, mac_addr);
424 	}
425 
426 	return 0;
427 }
428 
429 static int
430 eth_dev_handle_port_flow_ctrl(const char *cmd __rte_unused,
431 		const char *params,
432 		struct rte_tel_data *d)
433 {
434 	struct rte_eth_fc_conf fc_conf;
435 	uint16_t port_id;
436 	char *end_param;
437 	bool rx_fc_en;
438 	bool tx_fc_en;
439 	int ret;
440 
441 	ret = eth_dev_parse_port_params(params, &port_id, &end_param, false);
442 	if (ret < 0)
443 		return ret;
444 
445 	ret = rte_eth_dev_flow_ctrl_get(port_id, &fc_conf);
446 	if (ret != 0) {
447 		RTE_ETHDEV_LOG(ERR,
448 			"Failed to get flow ctrl info, ret = %d\n", ret);
449 		return ret;
450 	}
451 
452 	rx_fc_en = fc_conf.mode == RTE_ETH_FC_RX_PAUSE ||
453 		   fc_conf.mode == RTE_ETH_FC_FULL;
454 	tx_fc_en = fc_conf.mode == RTE_ETH_FC_TX_PAUSE ||
455 		   fc_conf.mode == RTE_ETH_FC_FULL;
456 
457 	rte_tel_data_start_dict(d);
458 	rte_tel_data_add_dict_uint_hex(d, "high_waterline", fc_conf.high_water, 0);
459 	rte_tel_data_add_dict_uint_hex(d, "low_waterline", fc_conf.low_water, 0);
460 	rte_tel_data_add_dict_uint_hex(d, "pause_time", fc_conf.pause_time, 0);
461 	rte_tel_data_add_dict_string(d, "send_xon", fc_conf.send_xon ? "on" : "off");
462 	rte_tel_data_add_dict_string(d, "mac_ctrl_frame_fwd",
463 			fc_conf.mac_ctrl_frame_fwd ? "on" : "off");
464 	rte_tel_data_add_dict_string(d, "rx_pause", rx_fc_en ? "on" : "off");
465 	rte_tel_data_add_dict_string(d, "tx_pause", tx_fc_en ? "on" : "off");
466 	rte_tel_data_add_dict_string(d, "autoneg", fc_conf.autoneg ? "on" : "off");
467 
468 	return 0;
469 }
470 
471 static int
472 ethdev_parse_queue_params(const char *params, bool is_rx,
473 		uint16_t *port_id, uint16_t *queue_id)
474 {
475 	struct rte_eth_dev *dev;
476 	const char *qid_param;
477 	uint16_t nb_queues;
478 	char *end_param;
479 	uint64_t qid;
480 	int ret;
481 
482 	ret = eth_dev_parse_port_params(params, port_id, &end_param, true);
483 	if (ret < 0)
484 		return ret;
485 
486 	dev = &rte_eth_devices[*port_id];
487 	nb_queues = is_rx ? dev->data->nb_rx_queues : dev->data->nb_tx_queues;
488 	if (nb_queues == 1 && *end_param == '\0')
489 		qid = 0;
490 	else {
491 		qid_param = strtok(end_param, ",");
492 		if (!qid_param || strlen(qid_param) == 0 || !isdigit(*qid_param))
493 			return -EINVAL;
494 
495 		qid = strtoul(qid_param, &end_param, 0);
496 	}
497 	if (*end_param != '\0')
498 		RTE_ETHDEV_LOG(NOTICE,
499 			"Extra parameters passed to ethdev telemetry command, ignoring\n");
500 
501 	if (qid >= UINT16_MAX)
502 		return -EINVAL;
503 
504 	*queue_id = qid;
505 	return 0;
506 }
507 
508 static int
509 eth_dev_add_burst_mode(uint16_t port_id, uint16_t queue_id,
510 			bool is_rx, struct rte_tel_data *d)
511 {
512 	struct rte_eth_burst_mode mode;
513 	int ret;
514 
515 	if (is_rx)
516 		ret = rte_eth_rx_burst_mode_get(port_id, queue_id, &mode);
517 	else
518 		ret = rte_eth_tx_burst_mode_get(port_id, queue_id, &mode);
519 
520 	if (ret == -ENOTSUP)
521 		return 0;
522 
523 	if (ret != 0) {
524 		RTE_ETHDEV_LOG(ERR,
525 			"Failed to get burst mode for port %u\n", port_id);
526 		return ret;
527 	}
528 
529 	rte_tel_data_add_dict_uint(d, "burst_flags", mode.flags);
530 	rte_tel_data_add_dict_string(d, "burst_mode", mode.info);
531 	return 0;
532 }
533 
534 static int
535 eth_dev_handle_port_rxq(const char *cmd __rte_unused,
536 		const char *params,
537 		struct rte_tel_data *d)
538 {
539 	struct rte_eth_thresh *rx_thresh;
540 	struct rte_eth_rxconf *rxconf;
541 	struct rte_eth_rxq_info qinfo;
542 	struct rte_tel_data *offload;
543 	uint16_t port_id, queue_id;
544 	int ret;
545 
546 	ret = ethdev_parse_queue_params(params, true, &port_id, &queue_id);
547 	if (ret != 0)
548 		return ret;
549 
550 	ret = rte_eth_rx_queue_info_get(port_id, queue_id, &qinfo);
551 	if (ret != 0)
552 		return ret;
553 
554 	rte_tel_data_start_dict(d);
555 	rte_tel_data_add_dict_string(d, "mempool_name", qinfo.mp->name);
556 	rte_tel_data_add_dict_uint(d, "socket_id", qinfo.mp->socket_id);
557 
558 	rx_thresh = &qinfo.conf.rx_thresh;
559 	rte_tel_data_add_dict_uint(d, "host_threshold", rx_thresh->hthresh);
560 	rte_tel_data_add_dict_uint(d, "prefetch_threshold", rx_thresh->pthresh);
561 	rte_tel_data_add_dict_uint(d, "writeback_threshold", rx_thresh->wthresh);
562 
563 	rxconf = &qinfo.conf;
564 	rte_tel_data_add_dict_uint(d, "free_threshold", rxconf->rx_free_thresh);
565 	rte_tel_data_add_dict_string(d, "rx_drop_en",
566 			rxconf->rx_drop_en == 0 ? "off" : "on");
567 	rte_tel_data_add_dict_string(d, "deferred_start",
568 			rxconf->rx_deferred_start == 0 ? "off" : "on");
569 	rte_tel_data_add_dict_uint(d, "rx_nseg", rxconf->rx_nseg);
570 	rte_tel_data_add_dict_uint(d, "share_group", rxconf->share_group);
571 	rte_tel_data_add_dict_uint(d, "share_qid", rxconf->share_qid);
572 
573 	offload = rte_tel_data_alloc();
574 	if (offload == NULL)
575 		return -ENOMEM;
576 
577 	eth_dev_parse_rx_offloads(rxconf->offloads, offload);
578 	rte_tel_data_add_dict_container(d, "offloads", offload, 0);
579 
580 	rte_tel_data_add_dict_uint(d, "rx_nmempool", rxconf->rx_nmempool);
581 
582 	rte_tel_data_add_dict_string(d, "scattered_rx",
583 			qinfo.scattered_rx == 0 ? "off" : "on");
584 	rte_tel_data_add_dict_uint(d, "queue_state", qinfo.queue_state);
585 	rte_tel_data_add_dict_uint(d, "nb_desc", qinfo.nb_desc);
586 	rte_tel_data_add_dict_uint(d, "rx_buf_size", qinfo.rx_buf_size);
587 	rte_tel_data_add_dict_uint(d, "avail_thresh", qinfo.avail_thresh);
588 
589 	ret = eth_dev_add_burst_mode(port_id, queue_id, true, d);
590 	if (ret != 0)
591 		rte_tel_data_free(offload);
592 
593 	return ret;
594 }
595 
596 static int
597 eth_dev_handle_port_txq(const char *cmd __rte_unused,
598 		const char *params,
599 		struct rte_tel_data *d)
600 {
601 	struct rte_eth_thresh *tx_thresh;
602 	struct rte_eth_txconf *txconf;
603 	struct rte_eth_txq_info qinfo;
604 	struct rte_tel_data *offload;
605 	uint16_t port_id, queue_id;
606 	int ret;
607 
608 	ret = ethdev_parse_queue_params(params, false, &port_id, &queue_id);
609 	if (ret != 0)
610 		return ret;
611 
612 	ret = rte_eth_tx_queue_info_get(port_id, queue_id, &qinfo);
613 	if (ret != 0)
614 		return ret;
615 
616 	rte_tel_data_start_dict(d);
617 	tx_thresh = &qinfo.conf.tx_thresh;
618 	txconf = &qinfo.conf;
619 	rte_tel_data_add_dict_uint(d, "host_threshold", tx_thresh->hthresh);
620 	rte_tel_data_add_dict_uint(d, "prefetch_threshold", tx_thresh->pthresh);
621 	rte_tel_data_add_dict_uint(d, "writeback_threshold", tx_thresh->wthresh);
622 	rte_tel_data_add_dict_uint(d, "rs_threshold", txconf->tx_rs_thresh);
623 	rte_tel_data_add_dict_uint(d, "free_threshold", txconf->tx_free_thresh);
624 	rte_tel_data_add_dict_string(d, "deferred_start",
625 			txconf->tx_deferred_start == 0 ? "off" : "on");
626 
627 	offload = rte_tel_data_alloc();
628 	if (offload == NULL)
629 		return -ENOMEM;
630 
631 	eth_dev_parse_tx_offloads(txconf->offloads, offload);
632 	rte_tel_data_add_dict_container(d, "offloads", offload, 0);
633 
634 	rte_tel_data_add_dict_uint(d, "queue_state", qinfo.queue_state);
635 	rte_tel_data_add_dict_uint(d, "nb_desc", qinfo.nb_desc);
636 
637 	ret = eth_dev_add_burst_mode(port_id, queue_id, false, d);
638 	if (ret != 0)
639 		rte_tel_data_free(offload);
640 
641 	return 0;
642 }
643 
644 static int
645 eth_dev_add_dcb_tc(struct rte_eth_dcb_info *dcb_info, struct rte_tel_data *d)
646 {
647 	struct rte_tel_data *tcds[RTE_ETH_DCB_NUM_TCS] = {NULL};
648 	struct rte_eth_dcb_tc_queue_mapping *tcq;
649 	char bw_percent[RTE_TEL_MAX_STRING_LEN];
650 	char name[RTE_TEL_MAX_STRING_LEN];
651 	struct rte_tel_data *tcd;
652 	uint32_t i;
653 
654 	for (i = 0; i < dcb_info->nb_tcs; i++) {
655 		tcd = rte_tel_data_alloc();
656 		if (tcd == NULL) {
657 			while (i-- > 0)
658 				rte_tel_data_free(tcds[i]);
659 			return -ENOMEM;
660 		}
661 
662 		tcds[i] = tcd;
663 		rte_tel_data_start_dict(tcd);
664 
665 		rte_tel_data_add_dict_uint(tcd, "priority", dcb_info->prio_tc[i]);
666 		snprintf(bw_percent, RTE_TEL_MAX_STRING_LEN,
667 			"%u%%", dcb_info->tc_bws[i]);
668 		rte_tel_data_add_dict_string(tcd, "bw_percent", bw_percent);
669 
670 		tcq = &dcb_info->tc_queue;
671 		rte_tel_data_add_dict_uint(tcd, "rxq_base", tcq->tc_rxq[0][i].base);
672 		rte_tel_data_add_dict_uint(tcd, "txq_base", tcq->tc_txq[0][i].base);
673 		rte_tel_data_add_dict_uint(tcd, "nb_rxq", tcq->tc_rxq[0][i].nb_queue);
674 		rte_tel_data_add_dict_uint(tcd, "nb_txq", tcq->tc_txq[0][i].nb_queue);
675 
676 		snprintf(name, RTE_TEL_MAX_STRING_LEN, "tc%u", i);
677 		rte_tel_data_add_dict_container(d, name, tcd, 0);
678 	}
679 
680 	return 0;
681 }
682 
683 static int
684 eth_dev_add_dcb_info(uint16_t port_id, struct rte_tel_data *d)
685 {
686 	struct rte_eth_dcb_info dcb_info;
687 	int ret;
688 
689 	ret = rte_eth_dev_get_dcb_info(port_id, &dcb_info);
690 	if (ret != 0) {
691 		RTE_ETHDEV_LOG(ERR,
692 			"Failed to get dcb info, ret = %d\n", ret);
693 		return ret;
694 	}
695 
696 	rte_tel_data_start_dict(d);
697 	rte_tel_data_add_dict_uint(d, "tc_num", dcb_info.nb_tcs);
698 
699 	if (dcb_info.nb_tcs > 0)
700 		return eth_dev_add_dcb_tc(&dcb_info, d);
701 
702 	return 0;
703 }
704 
705 static int
706 eth_dev_handle_port_dcb(const char *cmd __rte_unused,
707 		const char *params,
708 		struct rte_tel_data *d)
709 {
710 	uint16_t port_id;
711 	char *end_param;
712 	int ret;
713 
714 	ret = eth_dev_parse_port_params(params, &port_id, &end_param, false);
715 	if (ret < 0)
716 		return ret;
717 
718 	return eth_dev_add_dcb_info(port_id, d);
719 }
720 
721 static int
722 eth_dev_add_rss_info(struct rte_eth_rss_conf *rss_conf, struct rte_tel_data *d)
723 {
724 	const uint32_t key_len = rss_conf->rss_key_len * 2 + 1;
725 	char *rss_key;
726 	char key[3]; /* FF\0 */
727 	uint32_t i;
728 	int ret;
729 
730 	rss_key = malloc(key_len);
731 	if (rss_key == NULL)
732 		return -ENOMEM;
733 
734 	rte_tel_data_start_dict(d);
735 	rte_tel_data_add_dict_uint_hex(d, "rss_hf", rss_conf->rss_hf, 0);
736 	rte_tel_data_add_dict_uint(d, "rss_key_len", rss_conf->rss_key_len);
737 
738 	memset(rss_key, 0, key_len);
739 	for (i = 0; i < rss_conf->rss_key_len; i++) {
740 		ret = snprintf(key, 3, "%02x", rss_conf->rss_key[i]);
741 		if (ret < 0)
742 			goto free_rss_key;
743 		strlcat(rss_key, key, key_len);
744 	}
745 	ret = rte_tel_data_add_dict_string(d, "rss_key", rss_key);
746 
747 free_rss_key:
748 	free(rss_key);
749 
750 	return ret;
751 }
752 
753 static int
754 eth_dev_handle_port_rss_info(const char *cmd __rte_unused,
755 		const char *params,
756 		struct rte_tel_data *d)
757 {
758 	struct rte_eth_dev_info dev_info;
759 	struct rte_eth_rss_conf rss_conf;
760 	uint8_t key_len;
761 	uint16_t port_id;
762 	char *end_param;
763 	int ret;
764 
765 	ret = eth_dev_parse_port_params(params, &port_id, &end_param, false);
766 	if (ret < 0)
767 		return ret;
768 
769 	ret = rte_eth_dev_info_get(port_id, &dev_info);
770 	if (ret != 0) {
771 		RTE_ETHDEV_LOG(ERR,
772 			"Failed to get device info, ret = %d\n", ret);
773 		return ret;
774 	}
775 
776 	key_len = dev_info.hash_key_size ? dev_info.hash_key_size : 40;
777 	rss_conf.rss_key_len = key_len;
778 	rss_conf.rss_key = malloc(key_len);
779 	if (rss_conf.rss_key == NULL)
780 		return -ENOMEM;
781 
782 	ret = rte_eth_dev_rss_hash_conf_get(port_id, &rss_conf);
783 	if (ret != 0) {
784 		free(rss_conf.rss_key);
785 		return ret;
786 	}
787 
788 	ret = eth_dev_add_rss_info(&rss_conf, d);
789 	free(rss_conf.rss_key);
790 	return ret;
791 }
792 
793 static const char *
794 eth_dev_fec_capa_to_string(uint32_t fec_capa)
795 {
796 	uint32_t i;
797 
798 	for (i = 0; i < RTE_DIM(rte_eth_fec_capa_name); i++) {
799 		if ((fec_capa & rte_eth_fec_capa_name[i].capa) != 0)
800 			return rte_eth_fec_capa_name[i].name;
801 	}
802 
803 	return "unknown";
804 }
805 
806 static void
807 eth_dev_fec_capas_to_string(uint32_t fec_capa, char *fec_name, uint32_t len)
808 {
809 	bool valid = false;
810 	size_t count = 0;
811 	uint32_t i;
812 
813 	for (i = 0; i < RTE_DIM(rte_eth_fec_capa_name); i++) {
814 		if ((fec_capa & rte_eth_fec_capa_name[i].capa) != 0) {
815 			strlcat(fec_name, rte_eth_fec_capa_name[i].name, len);
816 			count = strlcat(fec_name, " ", len);
817 			valid = true;
818 		}
819 	}
820 
821 	if (!valid)
822 		count = snprintf(fec_name, len, "unknown ");
823 
824 	if (count >= len) {
825 		RTE_ETHDEV_LOG(WARNING, "FEC capa names may be truncated\n");
826 		count = len;
827 	}
828 
829 	fec_name[count - 1] = '\0';
830 }
831 
832 static int
833 eth_dev_get_fec_capability(uint16_t port_id, struct rte_tel_data *d)
834 {
835 	struct rte_eth_fec_capa *speed_fec_capa;
836 	char fec_name[RTE_TEL_MAX_STRING_LEN];
837 	char speed[RTE_TEL_MAX_STRING_LEN];
838 	uint32_t capa_num;
839 	uint32_t i, j;
840 	int ret;
841 
842 	ret = rte_eth_fec_get_capability(port_id, NULL, 0);
843 	if (ret <= 0)
844 		return ret == 0 ? -EINVAL : ret;
845 
846 	capa_num = ret;
847 	speed_fec_capa = calloc(capa_num, sizeof(struct rte_eth_fec_capa));
848 	if (speed_fec_capa == NULL)
849 		return -ENOMEM;
850 
851 	ret = rte_eth_fec_get_capability(port_id, speed_fec_capa, capa_num);
852 	if (ret <= 0) {
853 		ret = ret == 0 ? -EINVAL : ret;
854 		goto out;
855 	}
856 
857 	for (i = 0; i < capa_num; i++) {
858 		memset(fec_name, 0, RTE_TEL_MAX_STRING_LEN);
859 		eth_dev_fec_capas_to_string(speed_fec_capa[i].capa, fec_name,
860 					    RTE_TEL_MAX_STRING_LEN);
861 
862 		memset(speed, 0, RTE_TEL_MAX_STRING_LEN);
863 		ret = snprintf(speed, RTE_TEL_MAX_STRING_LEN, "%s",
864 			rte_eth_link_speed_to_str(speed_fec_capa[i].speed));
865 		if (ret < 0)
866 			goto out;
867 
868 		for (j = 0; j < strlen(speed); j++) {
869 			if (speed[j] == ' ')
870 				speed[j] = '_';
871 		}
872 
873 		rte_tel_data_add_dict_string(d, speed, fec_name);
874 	}
875 
876 out:
877 	free(speed_fec_capa);
878 	return ret > 0 ? 0 : ret;
879 }
880 
881 static int
882 eth_dev_handle_port_fec(const char *cmd __rte_unused,
883 		const char *params,
884 		struct rte_tel_data *d)
885 {
886 	struct rte_tel_data *fec_capas;
887 	uint32_t fec_mode;
888 	uint16_t port_id;
889 	char *end_param;
890 	int ret;
891 
892 	ret = eth_dev_parse_port_params(params, &port_id, &end_param, false);
893 	if (ret < 0)
894 		return ret;
895 
896 	ret = rte_eth_fec_get(port_id, &fec_mode);
897 	if (ret != 0)
898 		return ret;
899 
900 	rte_tel_data_start_dict(d);
901 	rte_tel_data_add_dict_string(d, "fec_mode",
902 				     eth_dev_fec_capa_to_string(fec_mode));
903 
904 	fec_capas = rte_tel_data_alloc();
905 	if (fec_capas == NULL)
906 		return -ENOMEM;
907 
908 	rte_tel_data_start_dict(fec_capas);
909 	ret = eth_dev_get_fec_capability(port_id, fec_capas);
910 	if (ret != 0) {
911 		rte_tel_data_free(fec_capas);
912 		return ret;
913 	}
914 
915 	rte_tel_data_add_dict_container(d, "fec_capability", fec_capas, 0);
916 	return 0;
917 }
918 
919 static int
920 eth_dev_add_vlan_id(int port_id, struct rte_tel_data *d)
921 {
922 	struct rte_tel_data *vlan_blks[64] = {NULL};
923 	uint16_t vlan_num, vidx, vbit, num_blks;
924 	char blk_name[RTE_TEL_MAX_STRING_LEN];
925 	struct rte_vlan_filter_conf *vfc;
926 	struct rte_tel_data *vlan_blk;
927 	struct rte_tel_data *vd;
928 	uint64_t bit_width;
929 	uint64_t vlan_id;
930 
931 	vd = rte_tel_data_alloc();
932 	if (vd == NULL)
933 		return -ENOMEM;
934 
935 	vfc = &rte_eth_devices[port_id].data->vlan_filter_conf;
936 	bit_width = CHAR_BIT * sizeof(uint64_t);
937 	vlan_num = 0;
938 	num_blks = 0;
939 
940 	rte_tel_data_start_dict(vd);
941 	for (vidx = 0; vidx < RTE_DIM(vfc->ids); vidx++) {
942 		if (vfc->ids[vidx] == 0)
943 			continue;
944 
945 		vlan_blk = rte_tel_data_alloc();
946 		if (vlan_blk == NULL)
947 			goto free_all;
948 
949 		vlan_blks[num_blks] = vlan_blk;
950 		num_blks++;
951 		snprintf(blk_name, RTE_TEL_MAX_STRING_LEN, "vlan_%"PRIu64"_to_%"PRIu64"",
952 			 bit_width * vidx, bit_width * (vidx + 1) - 1);
953 		rte_tel_data_start_array(vlan_blk, RTE_TEL_UINT_VAL);
954 		rte_tel_data_add_dict_container(vd, blk_name, vlan_blk, 0);
955 
956 		for (vbit = 0; vbit < bit_width; vbit++) {
957 			if ((vfc->ids[vidx] & RTE_BIT64(vbit)) == 0)
958 				continue;
959 
960 			vlan_id = bit_width * vidx + vbit;
961 			rte_tel_data_add_array_uint(vlan_blk, vlan_id);
962 			vlan_num++;
963 		}
964 	}
965 
966 	rte_tel_data_add_dict_uint(d, "vlan_num", vlan_num);
967 	rte_tel_data_add_dict_container(d, "vlan_ids", vd, 0);
968 
969 	return 0;
970 
971 free_all:
972 	while (num_blks-- > 0)
973 		rte_tel_data_free(vlan_blks[num_blks]);
974 
975 	rte_tel_data_free(vd);
976 	return -ENOMEM;
977 }
978 
979 static int
980 eth_dev_handle_port_vlan(const char *cmd __rte_unused,
981 		const char *params,
982 		struct rte_tel_data *d)
983 {
984 	struct rte_eth_txmode *txmode;
985 	struct rte_eth_conf dev_conf;
986 	uint16_t port_id;
987 	int offload, ret;
988 	char *end_param;
989 
990 	ret = eth_dev_parse_port_params(params, &port_id, &end_param, false);
991 	if (ret < 0)
992 		return ret;
993 
994 	ret = rte_eth_dev_conf_get(port_id, &dev_conf);
995 	if (ret != 0) {
996 		RTE_ETHDEV_LOG(ERR,
997 			"Failed to get device configuration, ret = %d\n", ret);
998 		return ret;
999 	}
1000 
1001 	txmode = &dev_conf.txmode;
1002 	rte_tel_data_start_dict(d);
1003 	rte_tel_data_add_dict_uint(d, "pvid", txmode->pvid);
1004 	rte_tel_data_add_dict_uint(d, "hw_vlan_reject_tagged",
1005 		txmode->hw_vlan_reject_tagged);
1006 	rte_tel_data_add_dict_uint(d, "hw_vlan_reject_untagged",
1007 		txmode->hw_vlan_reject_untagged);
1008 	rte_tel_data_add_dict_uint(d, "hw_vlan_insert_pvid",
1009 		txmode->hw_vlan_insert_pvid);
1010 
1011 	offload = rte_eth_dev_get_vlan_offload(port_id);
1012 	rte_tel_data_add_dict_string(d, "VLAN_STRIP",
1013 		((offload & RTE_ETH_VLAN_STRIP_OFFLOAD) != 0) ? "on" : "off");
1014 	rte_tel_data_add_dict_string(d, "VLAN_EXTEND",
1015 		((offload & RTE_ETH_VLAN_EXTEND_OFFLOAD) != 0) ? "on" : "off");
1016 	rte_tel_data_add_dict_string(d, "QINQ_STRIP",
1017 		((offload & RTE_ETH_QINQ_STRIP_OFFLOAD) != 0) ? "on" : "off");
1018 	rte_tel_data_add_dict_string(d, "VLAN_FILTER",
1019 		((offload & RTE_ETH_VLAN_FILTER_OFFLOAD) != 0) ? "on" : "off");
1020 
1021 	return eth_dev_add_vlan_id(port_id, d);
1022 }
1023 
1024 RTE_INIT(ethdev_init_telemetry)
1025 {
1026 	rte_telemetry_register_cmd("/ethdev/list", eth_dev_handle_port_list,
1027 			"Returns list of available ethdev ports. Takes no parameters");
1028 	rte_telemetry_register_cmd("/ethdev/stats", eth_dev_handle_port_stats,
1029 			"Returns the common stats for a port. Parameters: int port_id");
1030 	rte_telemetry_register_cmd("/ethdev/xstats", eth_dev_handle_port_xstats,
1031 			"Returns the extended stats for a port. Parameters: int port_id,hide_zero=true|false(Optional for indicates hide zero xstats)");
1032 #ifndef RTE_EXEC_ENV_WINDOWS
1033 	rte_telemetry_register_cmd("/ethdev/dump_priv", eth_dev_handle_port_dump_priv,
1034 			"Returns dump private information for a port. Parameters: int port_id");
1035 #endif
1036 	rte_telemetry_register_cmd("/ethdev/link_status",
1037 			eth_dev_handle_port_link_status,
1038 			"Returns the link status for a port. Parameters: int port_id");
1039 	rte_telemetry_register_cmd("/ethdev/info", eth_dev_handle_port_info,
1040 			"Returns the device info for a port. Parameters: int port_id");
1041 	rte_telemetry_register_cmd("/ethdev/module_eeprom", eth_dev_handle_port_module_eeprom,
1042 			"Returns module EEPROM info with SFF specs. Parameters: int port_id");
1043 	rte_telemetry_register_cmd("/ethdev/macs", eth_dev_handle_port_macs,
1044 			"Returns the MAC addresses for a port. Parameters: int port_id");
1045 	rte_telemetry_register_cmd("/ethdev/flow_ctrl", eth_dev_handle_port_flow_ctrl,
1046 			"Returns flow ctrl info for a port. Parameters: int port_id");
1047 	rte_telemetry_register_cmd("/ethdev/rx_queue", eth_dev_handle_port_rxq,
1048 			"Returns Rx queue info for a port. Parameters: int port_id, int queue_id (Optional if only one queue)");
1049 	rte_telemetry_register_cmd("/ethdev/tx_queue", eth_dev_handle_port_txq,
1050 			"Returns Tx queue info for a port. Parameters: int port_id, int queue_id (Optional if only one queue)");
1051 	rte_telemetry_register_cmd("/ethdev/dcb", eth_dev_handle_port_dcb,
1052 			"Returns DCB info for a port. Parameters: int port_id");
1053 	rte_telemetry_register_cmd("/ethdev/rss_info", eth_dev_handle_port_rss_info,
1054 			"Returns RSS info for a port. Parameters: int port_id");
1055 	rte_telemetry_register_cmd("/ethdev/fec", eth_dev_handle_port_fec,
1056 			"Returns FEC info for a port. Parameters: int port_id");
1057 	rte_telemetry_register_cmd("/ethdev/vlan", eth_dev_handle_port_vlan,
1058 			"Returns VLAN info for a port. Parameters: int port_id");
1059 }
1060