6906dc81ccc9e2d4195d5fd6bc31175a7a5b4f1f
[dpdk.git] / drivers / net / mlx5 / mlx5_stats.c
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 mlx5_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 mlx5_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 mlx5_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 mlx5_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 mlx5_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 mlx5_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 mlx5_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 mlx5_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 }