xref: /dpdk/drivers/net/mlx5/mlx5_stats.c (revision 89f0711f9ddfb5822da9d34f384b92f72a61c4dc)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright 2015 6WIND S.A.
5  *   Copyright 2015 Mellanox.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of 6WIND S.A. nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <linux/sockios.h>
35 #include <linux/ethtool.h>
36 
37 #include <rte_ethdev_driver.h>
38 #include <rte_common.h>
39 #include <rte_malloc.h>
40 
41 #include "mlx5.h"
42 #include "mlx5_rxtx.h"
43 #include "mlx5_defs.h"
44 
45 struct mlx5_counter_ctrl {
46 	/* Name of the counter. */
47 	char dpdk_name[RTE_ETH_XSTATS_NAME_SIZE];
48 	/* Name of the counter on the device table. */
49 	char ctr_name[RTE_ETH_XSTATS_NAME_SIZE];
50 };
51 
52 static const struct mlx5_counter_ctrl mlx5_counters_init[] = {
53 	{
54 		.dpdk_name = "rx_port_unicast_bytes",
55 		.ctr_name = "rx_vport_unicast_bytes",
56 	},
57 	{
58 		.dpdk_name = "rx_port_multicast_bytes",
59 		.ctr_name = "rx_vport_multicast_bytes",
60 	},
61 	{
62 		.dpdk_name = "rx_port_broadcast_bytes",
63 		.ctr_name = "rx_vport_broadcast_bytes",
64 	},
65 	{
66 		.dpdk_name = "rx_port_unicast_packets",
67 		.ctr_name = "rx_vport_unicast_packets",
68 	},
69 	{
70 		.dpdk_name = "rx_port_multicast_packets",
71 		.ctr_name = "rx_vport_multicast_packets",
72 	},
73 	{
74 		.dpdk_name = "rx_port_broadcast_packets",
75 		.ctr_name = "rx_vport_broadcast_packets",
76 	},
77 	{
78 		.dpdk_name = "tx_port_unicast_bytes",
79 		.ctr_name = "tx_vport_unicast_bytes",
80 	},
81 	{
82 		.dpdk_name = "tx_port_multicast_bytes",
83 		.ctr_name = "tx_vport_multicast_bytes",
84 	},
85 	{
86 		.dpdk_name = "tx_port_broadcast_bytes",
87 		.ctr_name = "tx_vport_broadcast_bytes",
88 	},
89 	{
90 		.dpdk_name = "tx_port_unicast_packets",
91 		.ctr_name = "tx_vport_unicast_packets",
92 	},
93 	{
94 		.dpdk_name = "tx_port_multicast_packets",
95 		.ctr_name = "tx_vport_multicast_packets",
96 	},
97 	{
98 		.dpdk_name = "tx_port_broadcast_packets",
99 		.ctr_name = "tx_vport_broadcast_packets",
100 	},
101 	{
102 		.dpdk_name = "rx_wqe_err",
103 		.ctr_name = "rx_wqe_err",
104 	},
105 	{
106 		.dpdk_name = "rx_crc_errors_phy",
107 		.ctr_name = "rx_crc_errors_phy",
108 	},
109 	{
110 		.dpdk_name = "rx_in_range_len_errors_phy",
111 		.ctr_name = "rx_in_range_len_errors_phy",
112 	},
113 	{
114 		.dpdk_name = "rx_symbol_err_phy",
115 		.ctr_name = "rx_symbol_err_phy",
116 	},
117 	{
118 		.dpdk_name = "tx_errors_phy",
119 		.ctr_name = "tx_errors_phy",
120 	},
121 	{
122 		.dpdk_name = "rx_out_of_buffer",
123 		.ctr_name = "out_of_buffer",
124 	},
125 	{
126 		.dpdk_name = "tx_packets_phy",
127 		.ctr_name = "tx_packets_phy",
128 	},
129 	{
130 		.dpdk_name = "rx_packets_phy",
131 		.ctr_name = "rx_packets_phy",
132 	},
133 	{
134 		.dpdk_name = "tx_bytes_phy",
135 		.ctr_name = "tx_bytes_phy",
136 	},
137 	{
138 		.dpdk_name = "rx_bytes_phy",
139 		.ctr_name = "rx_bytes_phy",
140 	},
141 };
142 
143 static const unsigned int xstats_n = RTE_DIM(mlx5_counters_init);
144 
145 /**
146  * Read device counters table.
147  *
148  * @param priv
149  *   Pointer to private structure.
150  * @param[out] stats
151  *   Counters table output buffer.
152  *
153  * @return
154  *   0 on success and stats is filled, negative on error.
155  */
156 static int
157 priv_read_dev_counters(struct priv *priv, uint64_t *stats)
158 {
159 	struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
160 	unsigned int i;
161 	struct ifreq ifr;
162 	unsigned int stats_sz = xstats_ctrl->stats_n * sizeof(uint64_t);
163 	unsigned char et_stat_buf[sizeof(struct ethtool_stats) + stats_sz];
164 	struct ethtool_stats *et_stats = (struct ethtool_stats *)et_stat_buf;
165 
166 	et_stats->cmd = ETHTOOL_GSTATS;
167 	et_stats->n_stats = xstats_ctrl->stats_n;
168 	ifr.ifr_data = (caddr_t)et_stats;
169 	if (priv_ifreq(priv, SIOCETHTOOL, &ifr) != 0) {
170 		WARN("unable to read statistic values from device");
171 		return -1;
172 	}
173 	for (i = 0; i != xstats_n; ++i) {
174 		if (priv_is_ib_cntr(mlx5_counters_init[i].ctr_name))
175 			priv_get_cntr_sysfs(priv,
176 					    mlx5_counters_init[i].ctr_name,
177 					    &stats[i]);
178 		else
179 			stats[i] = (uint64_t)
180 				et_stats->data[xstats_ctrl->dev_table_idx[i]];
181 	}
182 	return 0;
183 }
184 
185 /**
186  * Query the number of statistics provided by ETHTOOL.
187  *
188  * @param priv
189  *   Pointer to private structure.
190  *
191  * @return
192  *   Number of statistics on success, -1 on error.
193  */
194 static int
195 priv_ethtool_get_stats_n(struct priv *priv) {
196 	struct ethtool_drvinfo drvinfo;
197 	struct ifreq ifr;
198 
199 	drvinfo.cmd = ETHTOOL_GDRVINFO;
200 	ifr.ifr_data = (caddr_t)&drvinfo;
201 	if (priv_ifreq(priv, SIOCETHTOOL, &ifr) != 0) {
202 		WARN("unable to query number of statistics");
203 		return -1;
204 	}
205 	return drvinfo.n_stats;
206 }
207 
208 /**
209  * Init the structures to read device counters.
210  *
211  * @param priv
212  *   Pointer to private structure.
213  */
214 void
215 priv_xstats_init(struct priv *priv)
216 {
217 	struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
218 	unsigned int i;
219 	unsigned int j;
220 	struct ifreq ifr;
221 	struct ethtool_gstrings *strings = NULL;
222 	unsigned int dev_stats_n;
223 	unsigned int str_sz;
224 
225 	dev_stats_n = priv_ethtool_get_stats_n(priv);
226 	if (dev_stats_n < 1) {
227 		WARN("no extended statistics available");
228 		return;
229 	}
230 	xstats_ctrl->stats_n = dev_stats_n;
231 	/* Allocate memory to grab stat names and values. */
232 	str_sz = dev_stats_n * ETH_GSTRING_LEN;
233 	strings = (struct ethtool_gstrings *)
234 		  rte_malloc("xstats_strings",
235 			     str_sz + sizeof(struct ethtool_gstrings), 0);
236 	if (!strings) {
237 		WARN("unable to allocate memory for xstats");
238 		return;
239 	}
240 	strings->cmd = ETHTOOL_GSTRINGS;
241 	strings->string_set = ETH_SS_STATS;
242 	strings->len = dev_stats_n;
243 	ifr.ifr_data = (caddr_t)strings;
244 	if (priv_ifreq(priv, SIOCETHTOOL, &ifr) != 0) {
245 		WARN("unable to get statistic names");
246 		goto free;
247 	}
248 	for (j = 0; j != xstats_n; ++j)
249 		xstats_ctrl->dev_table_idx[j] = dev_stats_n;
250 	for (i = 0; i != dev_stats_n; ++i) {
251 		const char *curr_string = (const char *)
252 			&strings->data[i * ETH_GSTRING_LEN];
253 
254 		for (j = 0; j != xstats_n; ++j) {
255 			if (!strcmp(mlx5_counters_init[j].ctr_name,
256 				    curr_string)) {
257 				xstats_ctrl->dev_table_idx[j] = i;
258 				break;
259 			}
260 		}
261 	}
262 	for (j = 0; j != xstats_n; ++j) {
263 		if (priv_is_ib_cntr(mlx5_counters_init[j].ctr_name))
264 			continue;
265 		if (xstats_ctrl->dev_table_idx[j] >= dev_stats_n) {
266 			WARN("counter \"%s\" is not recognized",
267 			     mlx5_counters_init[j].dpdk_name);
268 			goto free;
269 		}
270 	}
271 	/* Copy to base at first time. */
272 	assert(xstats_n <= MLX5_MAX_XSTATS);
273 	priv_read_dev_counters(priv, xstats_ctrl->base);
274 free:
275 	rte_free(strings);
276 }
277 
278 /**
279  * Get device extended statistics.
280  *
281  * @param priv
282  *   Pointer to private structure.
283  * @param[out] stats
284  *   Pointer to rte extended stats table.
285  *
286  * @return
287  *   Number of extended stats on success and stats is filled,
288  *   negative on error.
289  */
290 static int
291 priv_xstats_get(struct priv *priv, struct rte_eth_xstat *stats)
292 {
293 	struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
294 	unsigned int i;
295 	unsigned int n = xstats_n;
296 	uint64_t counters[n];
297 
298 	if (priv_read_dev_counters(priv, counters) < 0)
299 		return -1;
300 	for (i = 0; i != xstats_n; ++i) {
301 		stats[i].id = i;
302 		stats[i].value = (counters[i] - xstats_ctrl->base[i]);
303 	}
304 	return n;
305 }
306 
307 /**
308  * Reset device extended statistics.
309  *
310  * @param priv
311  *   Pointer to private structure.
312  */
313 static void
314 priv_xstats_reset(struct priv *priv)
315 {
316 	struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
317 	unsigned int i;
318 	unsigned int n = xstats_n;
319 	uint64_t counters[n];
320 
321 	if (priv_read_dev_counters(priv, counters) < 0)
322 		return;
323 	for (i = 0; i != n; ++i)
324 		xstats_ctrl->base[i] = counters[i];
325 }
326 
327 /**
328  * DPDK callback to get device statistics.
329  *
330  * @param dev
331  *   Pointer to Ethernet device structure.
332  * @param[out] stats
333  *   Stats structure output buffer.
334  */
335 int
336 mlx5_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
337 {
338 	struct priv *priv = dev->data->dev_private;
339 	struct rte_eth_stats tmp = {0};
340 	unsigned int i;
341 	unsigned int idx;
342 
343 	priv_lock(priv);
344 	/* Add software counters. */
345 	for (i = 0; (i != priv->rxqs_n); ++i) {
346 		struct mlx5_rxq_data *rxq = (*priv->rxqs)[i];
347 
348 		if (rxq == NULL)
349 			continue;
350 		idx = rxq->stats.idx;
351 		if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
352 #ifdef MLX5_PMD_SOFT_COUNTERS
353 			tmp.q_ipackets[idx] += rxq->stats.ipackets;
354 			tmp.q_ibytes[idx] += rxq->stats.ibytes;
355 #endif
356 			tmp.q_errors[idx] += (rxq->stats.idropped +
357 					      rxq->stats.rx_nombuf);
358 		}
359 #ifdef MLX5_PMD_SOFT_COUNTERS
360 		tmp.ipackets += rxq->stats.ipackets;
361 		tmp.ibytes += rxq->stats.ibytes;
362 #endif
363 		tmp.ierrors += rxq->stats.idropped;
364 		tmp.rx_nombuf += rxq->stats.rx_nombuf;
365 	}
366 	for (i = 0; (i != priv->txqs_n); ++i) {
367 		struct mlx5_txq_data *txq = (*priv->txqs)[i];
368 
369 		if (txq == NULL)
370 			continue;
371 		idx = txq->stats.idx;
372 		if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
373 #ifdef MLX5_PMD_SOFT_COUNTERS
374 			tmp.q_opackets[idx] += txq->stats.opackets;
375 			tmp.q_obytes[idx] += txq->stats.obytes;
376 #endif
377 			tmp.q_errors[idx] += txq->stats.oerrors;
378 		}
379 #ifdef MLX5_PMD_SOFT_COUNTERS
380 		tmp.opackets += txq->stats.opackets;
381 		tmp.obytes += txq->stats.obytes;
382 #endif
383 		tmp.oerrors += txq->stats.oerrors;
384 	}
385 #ifndef MLX5_PMD_SOFT_COUNTERS
386 	/* FIXME: retrieve and add hardware counters. */
387 #endif
388 	*stats = tmp;
389 	priv_unlock(priv);
390 	return 0;
391 }
392 
393 /**
394  * DPDK callback to clear device statistics.
395  *
396  * @param dev
397  *   Pointer to Ethernet device structure.
398  */
399 void
400 mlx5_stats_reset(struct rte_eth_dev *dev)
401 {
402 	struct priv *priv = dev->data->dev_private;
403 	unsigned int i;
404 	unsigned int idx;
405 
406 	priv_lock(priv);
407 	for (i = 0; (i != priv->rxqs_n); ++i) {
408 		if ((*priv->rxqs)[i] == NULL)
409 			continue;
410 		idx = (*priv->rxqs)[i]->stats.idx;
411 		(*priv->rxqs)[i]->stats =
412 			(struct mlx5_rxq_stats){ .idx = idx };
413 	}
414 	for (i = 0; (i != priv->txqs_n); ++i) {
415 		if ((*priv->txqs)[i] == NULL)
416 			continue;
417 		idx = (*priv->txqs)[i]->stats.idx;
418 		(*priv->txqs)[i]->stats =
419 			(struct mlx5_txq_stats){ .idx = idx };
420 	}
421 #ifndef MLX5_PMD_SOFT_COUNTERS
422 	/* FIXME: reset hardware counters. */
423 #endif
424 	priv_unlock(priv);
425 }
426 
427 /**
428  * DPDK callback to get extended device statistics.
429  *
430  * @param dev
431  *   Pointer to Ethernet device structure.
432  * @param[out] stats
433  *   Stats table output buffer.
434  * @param n
435  *   The size of the stats table.
436  *
437  * @return
438  *   Number of xstats on success, negative on failure.
439  */
440 int
441 mlx5_xstats_get(struct rte_eth_dev *dev,
442 		struct rte_eth_xstat *stats, unsigned int n)
443 {
444 	struct priv *priv = dev->data->dev_private;
445 	int ret = xstats_n;
446 
447 	if (n >= xstats_n && stats) {
448 		struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
449 		int stats_n;
450 
451 		priv_lock(priv);
452 		stats_n = priv_ethtool_get_stats_n(priv);
453 		if (stats_n < 0) {
454 			priv_unlock(priv);
455 			return -1;
456 		}
457 		if (xstats_ctrl->stats_n != stats_n)
458 			priv_xstats_init(priv);
459 		ret = priv_xstats_get(priv, stats);
460 		priv_unlock(priv);
461 	}
462 	return ret;
463 }
464 
465 /**
466  * DPDK callback to clear device extended statistics.
467  *
468  * @param dev
469  *   Pointer to Ethernet device structure.
470  */
471 void
472 mlx5_xstats_reset(struct rte_eth_dev *dev)
473 {
474 	struct priv *priv = dev->data->dev_private;
475 	struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
476 	int stats_n;
477 
478 	priv_lock(priv);
479 	stats_n = priv_ethtool_get_stats_n(priv);
480 	if (stats_n < 0)
481 		goto unlock;
482 	if (xstats_ctrl->stats_n != stats_n)
483 		priv_xstats_init(priv);
484 	priv_xstats_reset(priv);
485 unlock:
486 	priv_unlock(priv);
487 }
488 
489 /**
490  * DPDK callback to retrieve names of extended device statistics
491  *
492  * @param dev
493  *   Pointer to Ethernet device structure.
494  * @param[out] xstats_names
495  *   Buffer to insert names into.
496  * @param n
497  *   Number of names.
498  *
499  * @return
500  *   Number of xstats names.
501  */
502 int
503 mlx5_xstats_get_names(struct rte_eth_dev *dev,
504 		struct rte_eth_xstat_name *xstats_names, unsigned int n)
505 {
506 	struct priv *priv = dev->data->dev_private;
507 	unsigned int i;
508 
509 	if (n >= xstats_n && xstats_names) {
510 		priv_lock(priv);
511 		for (i = 0; i != xstats_n; ++i) {
512 			strncpy(xstats_names[i].name,
513 				mlx5_counters_init[i].dpdk_name,
514 				RTE_ETH_XSTATS_NAME_SIZE);
515 			xstats_names[i].name[RTE_ETH_XSTATS_NAME_SIZE - 1] = 0;
516 		}
517 		priv_unlock(priv);
518 	}
519 	return xstats_n;
520 }
521