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