xref: /dpdk/drivers/net/mlx5/mlx5_stats.c (revision 25d11a86c56d50947af33d0b79ede622809bd8b9)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2015 6WIND S.A.
3  * Copyright 2015 Mellanox Technologies, Ltd
4  */
5 
6 #include <inttypes.h>
7 #include <linux/sockios.h>
8 #include <linux/ethtool.h>
9 #include <stdint.h>
10 #include <stdio.h>
11 
12 #include <rte_ethdev_driver.h>
13 #include <rte_common.h>
14 #include <rte_malloc.h>
15 
16 #include "mlx5.h"
17 #include "mlx5_rxtx.h"
18 #include "mlx5_defs.h"
19 
20 static const struct mlx5_counter_ctrl mlx5_counters_init[] = {
21 	{
22 		.dpdk_name = "rx_port_unicast_bytes",
23 		.ctr_name = "rx_vport_unicast_bytes",
24 	},
25 	{
26 		.dpdk_name = "rx_port_multicast_bytes",
27 		.ctr_name = "rx_vport_multicast_bytes",
28 	},
29 	{
30 		.dpdk_name = "rx_port_broadcast_bytes",
31 		.ctr_name = "rx_vport_broadcast_bytes",
32 	},
33 	{
34 		.dpdk_name = "rx_port_unicast_packets",
35 		.ctr_name = "rx_vport_unicast_packets",
36 	},
37 	{
38 		.dpdk_name = "rx_port_multicast_packets",
39 		.ctr_name = "rx_vport_multicast_packets",
40 	},
41 	{
42 		.dpdk_name = "rx_port_broadcast_packets",
43 		.ctr_name = "rx_vport_broadcast_packets",
44 	},
45 	{
46 		.dpdk_name = "tx_port_unicast_bytes",
47 		.ctr_name = "tx_vport_unicast_bytes",
48 	},
49 	{
50 		.dpdk_name = "tx_port_multicast_bytes",
51 		.ctr_name = "tx_vport_multicast_bytes",
52 	},
53 	{
54 		.dpdk_name = "tx_port_broadcast_bytes",
55 		.ctr_name = "tx_vport_broadcast_bytes",
56 	},
57 	{
58 		.dpdk_name = "tx_port_unicast_packets",
59 		.ctr_name = "tx_vport_unicast_packets",
60 	},
61 	{
62 		.dpdk_name = "tx_port_multicast_packets",
63 		.ctr_name = "tx_vport_multicast_packets",
64 	},
65 	{
66 		.dpdk_name = "tx_port_broadcast_packets",
67 		.ctr_name = "tx_vport_broadcast_packets",
68 	},
69 	{
70 		.dpdk_name = "rx_wqe_err",
71 		.ctr_name = "rx_wqe_err",
72 	},
73 	{
74 		.dpdk_name = "rx_crc_errors_phy",
75 		.ctr_name = "rx_crc_errors_phy",
76 	},
77 	{
78 		.dpdk_name = "rx_in_range_len_errors_phy",
79 		.ctr_name = "rx_in_range_len_errors_phy",
80 	},
81 	{
82 		.dpdk_name = "rx_symbol_err_phy",
83 		.ctr_name = "rx_symbol_err_phy",
84 	},
85 	{
86 		.dpdk_name = "tx_errors_phy",
87 		.ctr_name = "tx_errors_phy",
88 	},
89 	{
90 		.dpdk_name = "rx_out_of_buffer",
91 		.ctr_name = "out_of_buffer",
92 		.ib = 1,
93 	},
94 	{
95 		.dpdk_name = "tx_packets_phy",
96 		.ctr_name = "tx_packets_phy",
97 	},
98 	{
99 		.dpdk_name = "rx_packets_phy",
100 		.ctr_name = "rx_packets_phy",
101 	},
102 	{
103 		.dpdk_name = "tx_discards_phy",
104 		.ctr_name = "tx_discards_phy",
105 	},
106 	{
107 		.dpdk_name = "rx_discards_phy",
108 		.ctr_name = "rx_discards_phy",
109 	},
110 	{
111 		.dpdk_name = "tx_bytes_phy",
112 		.ctr_name = "tx_bytes_phy",
113 	},
114 	{
115 		.dpdk_name = "rx_bytes_phy",
116 		.ctr_name = "rx_bytes_phy",
117 	},
118 	/* Representor only */
119 	{
120 		.dpdk_name = "rx_packets",
121 		.ctr_name = "vport_rx_packets",
122 	},
123 	{
124 		.dpdk_name = "rx_bytes",
125 		.ctr_name = "vport_rx_bytes",
126 	},
127 	{
128 		.dpdk_name = "tx_packets",
129 		.ctr_name = "vport_tx_packets",
130 	},
131 	{
132 		.dpdk_name = "tx_bytes",
133 		.ctr_name = "vport_tx_bytes",
134 	},
135 };
136 
137 static const unsigned int xstats_n = RTE_DIM(mlx5_counters_init);
138 
139 static inline void
140 mlx5_read_ib_stat(struct priv *priv, const char *ctr_name, uint64_t *stat)
141 {
142 	FILE *file;
143 	MKSTR(path, "%s/ports/1/hw_counters/%s",
144 		  priv->ibdev_path,
145 		  ctr_name);
146 
147 	file = fopen(path, "rb");
148 	if (file) {
149 		int n = fscanf(file, "%" SCNu64, stat);
150 
151 		fclose(file);
152 		if (n != 1)
153 			stat = 0;
154 	}
155 }
156 
157 /**
158  * Read device counters table.
159  *
160  * @param dev
161  *   Pointer to Ethernet device.
162  * @param[out] stats
163  *   Counters table output buffer.
164  *
165  * @return
166  *   0 on success and stats is filled, negative errno value otherwise and
167  *   rte_errno is set.
168  */
169 static int
170 mlx5_read_dev_counters(struct rte_eth_dev *dev, uint64_t *stats)
171 {
172 	struct priv *priv = dev->data->dev_private;
173 	struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
174 	unsigned int i;
175 	struct ifreq ifr;
176 	unsigned int stats_sz = xstats_ctrl->stats_n * sizeof(uint64_t);
177 	unsigned char et_stat_buf[sizeof(struct ethtool_stats) + stats_sz];
178 	struct ethtool_stats *et_stats = (struct ethtool_stats *)et_stat_buf;
179 	int ret;
180 
181 	et_stats->cmd = ETHTOOL_GSTATS;
182 	et_stats->n_stats = xstats_ctrl->stats_n;
183 	ifr.ifr_data = (caddr_t)et_stats;
184 	ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
185 	if (ret) {
186 		DRV_LOG(WARNING,
187 			"port %u unable to read statistic values from device",
188 			dev->data->port_id);
189 		return ret;
190 	}
191 	for (i = 0; i != xstats_ctrl->mlx5_stats_n; ++i) {
192 		if (xstats_ctrl->info[i].ib) {
193 			mlx5_read_ib_stat(priv, xstats_ctrl->info[i].ctr_name,
194 					  &stats[i]);
195 		} else {
196 			stats[i] = (uint64_t)
197 				et_stats->data[xstats_ctrl->dev_table_idx[i]];
198 		}
199 	}
200 	return 0;
201 }
202 
203 /**
204  * Query the number of statistics provided by ETHTOOL.
205  *
206  * @param dev
207  *   Pointer to Ethernet device.
208  *
209  * @return
210  *   Number of statistics on success, negative errno value otherwise and
211  *   rte_errno is set.
212  */
213 static int
214 mlx5_ethtool_get_stats_n(struct rte_eth_dev *dev) {
215 	struct ethtool_drvinfo drvinfo;
216 	struct ifreq ifr;
217 	int ret;
218 
219 	drvinfo.cmd = ETHTOOL_GDRVINFO;
220 	ifr.ifr_data = (caddr_t)&drvinfo;
221 	ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
222 	if (ret) {
223 		DRV_LOG(WARNING, "port %u unable to query number of statistics",
224 			dev->data->port_id);
225 		return ret;
226 	}
227 	return drvinfo.n_stats;
228 }
229 
230 /**
231  * Init the structures to read device counters.
232  *
233  * @param dev
234  *   Pointer to Ethernet device.
235  */
236 void
237 mlx5_stats_init(struct rte_eth_dev *dev)
238 {
239 	struct priv *priv = dev->data->dev_private;
240 	struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
241 	struct mlx5_stats_ctrl *stats_ctrl = &priv->stats_ctrl;
242 	unsigned int i;
243 	unsigned int j;
244 	struct ifreq ifr;
245 	struct ethtool_gstrings *strings = NULL;
246 	unsigned int dev_stats_n;
247 	unsigned int str_sz;
248 	int ret;
249 
250 	/* So that it won't aggregate for each init. */
251 	xstats_ctrl->mlx5_stats_n = 0;
252 	ret = mlx5_ethtool_get_stats_n(dev);
253 	if (ret < 0) {
254 		DRV_LOG(WARNING, "port %u no extended statistics available",
255 			dev->data->port_id);
256 		return;
257 	}
258 	dev_stats_n = ret;
259 	/* Allocate memory to grab stat names and values. */
260 	str_sz = dev_stats_n * ETH_GSTRING_LEN;
261 	strings = (struct ethtool_gstrings *)
262 		  rte_malloc("xstats_strings",
263 			     str_sz + sizeof(struct ethtool_gstrings), 0);
264 	if (!strings) {
265 		DRV_LOG(WARNING, "port %u unable to allocate memory for xstats",
266 		     dev->data->port_id);
267 		return;
268 	}
269 	strings->cmd = ETHTOOL_GSTRINGS;
270 	strings->string_set = ETH_SS_STATS;
271 	strings->len = dev_stats_n;
272 	ifr.ifr_data = (caddr_t)strings;
273 	ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
274 	if (ret) {
275 		DRV_LOG(WARNING, "port %u unable to get statistic names",
276 			dev->data->port_id);
277 		goto free;
278 	}
279 	for (i = 0; i != dev_stats_n; ++i) {
280 		const char *curr_string = (const char *)
281 			&strings->data[i * ETH_GSTRING_LEN];
282 
283 		for (j = 0; j != xstats_n; ++j) {
284 			if (!strcmp(mlx5_counters_init[j].ctr_name,
285 				    curr_string)) {
286 				unsigned int idx = xstats_ctrl->mlx5_stats_n++;
287 
288 				xstats_ctrl->dev_table_idx[idx] = i;
289 				xstats_ctrl->info[idx] = mlx5_counters_init[j];
290 				break;
291 			}
292 		}
293 	}
294 	/* Add IB counters. */
295 	for (i = 0; i != xstats_n; ++i) {
296 		if (mlx5_counters_init[i].ib) {
297 			unsigned int idx = xstats_ctrl->mlx5_stats_n++;
298 
299 			xstats_ctrl->info[idx] = mlx5_counters_init[i];
300 		}
301 	}
302 	assert(xstats_ctrl->mlx5_stats_n <= MLX5_MAX_XSTATS);
303 	xstats_ctrl->stats_n = dev_stats_n;
304 	/* Copy to base at first time. */
305 	ret = mlx5_read_dev_counters(dev, xstats_ctrl->base);
306 	if (ret)
307 		DRV_LOG(ERR, "port %u cannot read device counters: %s",
308 			dev->data->port_id, strerror(rte_errno));
309 	mlx5_read_ib_stat(priv, "out_of_buffer", &stats_ctrl->imissed_base);
310 free:
311 	rte_free(strings);
312 }
313 
314 /**
315  * DPDK callback to get extended device statistics.
316  *
317  * @param dev
318  *   Pointer to Ethernet device.
319  * @param[out] stats
320  *   Pointer to rte extended stats table.
321  * @param n
322  *   The size of the stats table.
323  *
324  * @return
325  *   Number of extended stats on success and stats is filled,
326  *   negative on error and rte_errno is set.
327  */
328 int
329 mlx5_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstat *stats,
330 		unsigned int n)
331 {
332 	struct priv *priv = dev->data->dev_private;
333 	unsigned int i;
334 	uint64_t counters[n];
335 	struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
336 	uint16_t mlx5_stats_n = xstats_ctrl->mlx5_stats_n;
337 
338 	if (n >= mlx5_stats_n && stats) {
339 		int stats_n;
340 		int ret;
341 
342 		stats_n = mlx5_ethtool_get_stats_n(dev);
343 		if (stats_n < 0)
344 			return stats_n;
345 		if (xstats_ctrl->stats_n != stats_n)
346 			mlx5_stats_init(dev);
347 		ret = mlx5_read_dev_counters(dev, counters);
348 		if (ret)
349 			return ret;
350 		for (i = 0; i != mlx5_stats_n; ++i) {
351 			stats[i].id = i;
352 			stats[i].value = (counters[i] - xstats_ctrl->base[i]);
353 		}
354 	}
355 	return mlx5_stats_n;
356 }
357 
358 /**
359  * DPDK callback to get device statistics.
360  *
361  * @param dev
362  *   Pointer to Ethernet device structure.
363  * @param[out] stats
364  *   Stats structure output buffer.
365  *
366  * @return
367  *   0 on success and stats is filled, negative errno value otherwise and
368  *   rte_errno is set.
369  */
370 int
371 mlx5_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
372 {
373 	struct priv *priv = dev->data->dev_private;
374 	struct rte_eth_stats tmp;
375 	unsigned int i;
376 	unsigned int idx;
377 
378 	memset(&tmp, 0, sizeof(tmp));
379 	/* Add software counters. */
380 	for (i = 0; (i != priv->rxqs_n); ++i) {
381 		struct mlx5_rxq_data *rxq = (*priv->rxqs)[i];
382 
383 		if (rxq == NULL)
384 			continue;
385 		idx = rxq->stats.idx;
386 		if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
387 #ifdef MLX5_PMD_SOFT_COUNTERS
388 			tmp.q_ipackets[idx] += rxq->stats.ipackets;
389 			tmp.q_ibytes[idx] += rxq->stats.ibytes;
390 #endif
391 			tmp.q_errors[idx] += (rxq->stats.idropped +
392 					      rxq->stats.rx_nombuf);
393 		}
394 #ifdef MLX5_PMD_SOFT_COUNTERS
395 		tmp.ipackets += rxq->stats.ipackets;
396 		tmp.ibytes += rxq->stats.ibytes;
397 #endif
398 		tmp.ierrors += rxq->stats.idropped;
399 		tmp.rx_nombuf += rxq->stats.rx_nombuf;
400 	}
401 	for (i = 0; (i != priv->txqs_n); ++i) {
402 		struct mlx5_txq_data *txq = (*priv->txqs)[i];
403 
404 		if (txq == NULL)
405 			continue;
406 		idx = txq->stats.idx;
407 		if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
408 #ifdef MLX5_PMD_SOFT_COUNTERS
409 			tmp.q_opackets[idx] += txq->stats.opackets;
410 			tmp.q_obytes[idx] += txq->stats.obytes;
411 #endif
412 			tmp.q_errors[idx] += txq->stats.oerrors;
413 		}
414 #ifdef MLX5_PMD_SOFT_COUNTERS
415 		tmp.opackets += txq->stats.opackets;
416 		tmp.obytes += txq->stats.obytes;
417 #endif
418 		tmp.oerrors += txq->stats.oerrors;
419 	}
420 	mlx5_read_ib_stat(priv, "out_of_buffer", &tmp.imissed);
421 	tmp.imissed -= priv->stats_ctrl.imissed_base;
422 #ifndef MLX5_PMD_SOFT_COUNTERS
423 	/* FIXME: retrieve and add hardware counters. */
424 #endif
425 	*stats = tmp;
426 	return 0;
427 }
428 
429 /**
430  * DPDK callback to clear device statistics.
431  *
432  * @param dev
433  *   Pointer to Ethernet device structure.
434  */
435 void
436 mlx5_stats_reset(struct rte_eth_dev *dev)
437 {
438 	struct priv *priv = dev->data->dev_private;
439 	struct mlx5_stats_ctrl *stats_ctrl = &priv->stats_ctrl;
440 	unsigned int i;
441 	unsigned int idx;
442 
443 	for (i = 0; (i != priv->rxqs_n); ++i) {
444 		if ((*priv->rxqs)[i] == NULL)
445 			continue;
446 		idx = (*priv->rxqs)[i]->stats.idx;
447 		(*priv->rxqs)[i]->stats =
448 			(struct mlx5_rxq_stats){ .idx = idx };
449 	}
450 	for (i = 0; (i != priv->txqs_n); ++i) {
451 		if ((*priv->txqs)[i] == NULL)
452 			continue;
453 		idx = (*priv->txqs)[i]->stats.idx;
454 		(*priv->txqs)[i]->stats =
455 			(struct mlx5_txq_stats){ .idx = idx };
456 	}
457 	mlx5_read_ib_stat(priv, "out_of_buffer", &stats_ctrl->imissed_base);
458 #ifndef MLX5_PMD_SOFT_COUNTERS
459 	/* FIXME: reset hardware counters. */
460 #endif
461 }
462 
463 /**
464  * DPDK callback to clear device extended statistics.
465  *
466  * @param dev
467  *   Pointer to Ethernet device structure.
468  */
469 void
470 mlx5_xstats_reset(struct rte_eth_dev *dev)
471 {
472 	struct priv *priv = dev->data->dev_private;
473 	struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
474 	int stats_n;
475 	unsigned int i;
476 	unsigned int n = xstats_ctrl->mlx5_stats_n;
477 	uint64_t counters[n];
478 	int ret;
479 
480 	stats_n = mlx5_ethtool_get_stats_n(dev);
481 	if (stats_n < 0) {
482 		DRV_LOG(ERR, "port %u cannot get stats: %s", dev->data->port_id,
483 			strerror(-stats_n));
484 		return;
485 	}
486 	if (xstats_ctrl->stats_n != stats_n)
487 		mlx5_stats_init(dev);
488 	ret = mlx5_read_dev_counters(dev, counters);
489 	if (ret) {
490 		DRV_LOG(ERR, "port %u cannot read device counters: %s",
491 			dev->data->port_id, strerror(rte_errno));
492 		return;
493 	}
494 	for (i = 0; i != n; ++i)
495 		xstats_ctrl->base[i] = counters[i];
496 }
497 
498 /**
499  * DPDK callback to retrieve names of extended device statistics
500  *
501  * @param dev
502  *   Pointer to Ethernet device structure.
503  * @param[out] xstats_names
504  *   Buffer to insert names into.
505  * @param n
506  *   Number of names.
507  *
508  * @return
509  *   Number of xstats names.
510  */
511 int
512 mlx5_xstats_get_names(struct rte_eth_dev *dev __rte_unused,
513 		      struct rte_eth_xstat_name *xstats_names, unsigned int n)
514 {
515 	unsigned int i;
516 	struct priv *priv = dev->data->dev_private;
517 	struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
518 	unsigned int mlx5_xstats_n = xstats_ctrl->mlx5_stats_n;
519 
520 	if (n >= mlx5_xstats_n && xstats_names) {
521 		for (i = 0; i != mlx5_xstats_n; ++i) {
522 			strncpy(xstats_names[i].name,
523 				xstats_ctrl->info[i].dpdk_name,
524 				RTE_ETH_XSTATS_NAME_SIZE);
525 			xstats_names[i].name[RTE_ETH_XSTATS_NAME_SIZE - 1] = 0;
526 		}
527 	}
528 	return mlx5_xstats_n;
529 }
530