9b89b9bfd2bd122f8b0d481d1911d7982ebd8bdd
[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_bytes_phy",
104                 .ctr_name = "tx_bytes_phy",
105         },
106         {
107                 .dpdk_name = "rx_bytes_phy",
108                 .ctr_name = "rx_bytes_phy",
109         },
110 };
111
112 static const unsigned int xstats_n = RTE_DIM(mlx5_counters_init);
113
114 /**
115  * Read device counters table.
116  *
117  * @param dev
118  *   Pointer to Ethernet device.
119  * @param[out] stats
120  *   Counters table output buffer.
121  *
122  * @return
123  *   0 on success and stats is filled, negative errno value otherwise and
124  *   rte_errno is set.
125  */
126 static int
127 mlx5_read_dev_counters(struct rte_eth_dev *dev, uint64_t *stats)
128 {
129         struct priv *priv = dev->data->dev_private;
130         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
131         unsigned int i;
132         struct ifreq ifr;
133         unsigned int stats_sz = xstats_ctrl->stats_n * sizeof(uint64_t);
134         unsigned char et_stat_buf[sizeof(struct ethtool_stats) + stats_sz];
135         struct ethtool_stats *et_stats = (struct ethtool_stats *)et_stat_buf;
136         int ret;
137
138         et_stats->cmd = ETHTOOL_GSTATS;
139         et_stats->n_stats = xstats_ctrl->stats_n;
140         ifr.ifr_data = (caddr_t)et_stats;
141         ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr, 0);
142         if (ret) {
143                 DRV_LOG(WARNING,
144                         "port %u unable to read statistic values from device",
145                         dev->data->port_id);
146                 return ret;
147         }
148         for (i = 0; i != xstats_ctrl->mlx5_stats_n; ++i) {
149                 if (xstats_ctrl->info[i].ib) {
150                         FILE *file;
151                         MKSTR(path, "%s/ports/1/hw_counters/%s",
152                               priv->ibdev_path,
153                               xstats_ctrl->info[i].ctr_name);
154
155                         file = fopen(path, "rb");
156                         if (file) {
157                                 int n = fscanf(file, "%" SCNu64, &stats[i]);
158
159                                 fclose(file);
160                                 if (n != 1)
161                                         stats[i] = 0;
162                         }
163                 } else {
164                         stats[i] = (uint64_t)
165                                 et_stats->data[xstats_ctrl->dev_table_idx[i]];
166                 }
167         }
168         return 0;
169 }
170
171 /**
172  * Query the number of statistics provided by ETHTOOL.
173  *
174  * @param dev
175  *   Pointer to Ethernet device.
176  *
177  * @return
178  *   Number of statistics on success, negative errno value otherwise and
179  *   rte_errno is set.
180  */
181 static int
182 mlx5_ethtool_get_stats_n(struct rte_eth_dev *dev) {
183         struct ethtool_drvinfo drvinfo;
184         struct ifreq ifr;
185         int ret;
186
187         drvinfo.cmd = ETHTOOL_GDRVINFO;
188         ifr.ifr_data = (caddr_t)&drvinfo;
189         ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr, 0);
190         if (ret) {
191                 DRV_LOG(WARNING, "port %u unable to query number of statistics",
192                         dev->data->port_id);
193                 return ret;
194         }
195         return drvinfo.n_stats;
196 }
197
198 /**
199  * Init the structures to read device counters.
200  *
201  * @param dev
202  *   Pointer to Ethernet device.
203  */
204 void
205 mlx5_xstats_init(struct rte_eth_dev *dev)
206 {
207         struct priv *priv = dev->data->dev_private;
208         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
209         unsigned int i;
210         unsigned int j;
211         struct ifreq ifr;
212         struct ethtool_gstrings *strings = NULL;
213         unsigned int dev_stats_n;
214         unsigned int str_sz;
215         int ret;
216
217         /* So that it won't aggregate for each init. */
218         xstats_ctrl->mlx5_stats_n = 0;
219         ret = mlx5_ethtool_get_stats_n(dev);
220         if (ret < 0) {
221                 DRV_LOG(WARNING, "port %u no extended statistics available",
222                         dev->data->port_id);
223                 return;
224         }
225         dev_stats_n = ret;
226         /* Allocate memory to grab stat names and values. */
227         str_sz = dev_stats_n * ETH_GSTRING_LEN;
228         strings = (struct ethtool_gstrings *)
229                   rte_malloc("xstats_strings",
230                              str_sz + sizeof(struct ethtool_gstrings), 0);
231         if (!strings) {
232                 DRV_LOG(WARNING, "port %u unable to allocate memory for xstats",
233                      dev->data->port_id);
234                 return;
235         }
236         strings->cmd = ETHTOOL_GSTRINGS;
237         strings->string_set = ETH_SS_STATS;
238         strings->len = dev_stats_n;
239         ifr.ifr_data = (caddr_t)strings;
240         ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr, 0);
241         if (ret) {
242                 DRV_LOG(WARNING, "port %u unable to get statistic names",
243                         dev->data->port_id);
244                 goto free;
245         }
246         for (i = 0; i != dev_stats_n; ++i) {
247                 const char *curr_string = (const char *)
248                         &strings->data[i * ETH_GSTRING_LEN];
249
250                 for (j = 0; j != xstats_n; ++j) {
251                         if (!strcmp(mlx5_counters_init[j].ctr_name,
252                                     curr_string)) {
253                                 unsigned int idx = xstats_ctrl->mlx5_stats_n++;
254
255                                 xstats_ctrl->dev_table_idx[idx] = i;
256                                 xstats_ctrl->info[idx] = mlx5_counters_init[j];
257                                 break;
258                         }
259                 }
260         }
261         /* Add IB counters. */
262         for (i = 0; i != xstats_n; ++i) {
263                 if (mlx5_counters_init[i].ib) {
264                         unsigned int idx = xstats_ctrl->mlx5_stats_n++;
265
266                         xstats_ctrl->info[idx] = mlx5_counters_init[i];
267                 }
268         }
269         assert(xstats_ctrl->mlx5_stats_n <= MLX5_MAX_XSTATS);
270         xstats_ctrl->stats_n = dev_stats_n;
271         /* Copy to base at first time. */
272         ret = mlx5_read_dev_counters(dev, xstats_ctrl->base);
273         if (ret)
274                 DRV_LOG(ERR, "port %u cannot read device counters: %s",
275                         dev->data->port_id, strerror(rte_errno));
276 free:
277         rte_free(strings);
278 }
279
280 /**
281  * DPDK callback to get extended device statistics.
282  *
283  * @param dev
284  *   Pointer to Ethernet device.
285  * @param[out] stats
286  *   Pointer to rte extended stats table.
287  * @param n
288  *   The size of the stats table.
289  *
290  * @return
291  *   Number of extended stats on success and stats is filled,
292  *   negative on error and rte_errno is set.
293  */
294 int
295 mlx5_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstat *stats,
296                 unsigned int n)
297 {
298         struct priv *priv = dev->data->dev_private;
299         unsigned int i;
300         uint64_t counters[n];
301         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
302         uint16_t mlx5_stats_n = xstats_ctrl->mlx5_stats_n;
303
304         if (n >= mlx5_stats_n && stats) {
305                 int stats_n;
306                 int ret;
307
308                 stats_n = mlx5_ethtool_get_stats_n(dev);
309                 if (stats_n < 0)
310                         return stats_n;
311                 if (xstats_ctrl->stats_n != stats_n)
312                         mlx5_xstats_init(dev);
313                 ret = mlx5_read_dev_counters(dev, counters);
314                 if (ret)
315                         return ret;
316                 for (i = 0; i != mlx5_stats_n; ++i) {
317                         stats[i].id = i;
318                         stats[i].value = (counters[i] - xstats_ctrl->base[i]);
319                 }
320         }
321         return mlx5_stats_n;
322 }
323
324 /**
325  * DPDK callback to get device statistics.
326  *
327  * @param dev
328  *   Pointer to Ethernet device structure.
329  * @param[out] stats
330  *   Stats structure output buffer.
331  *
332  * @return
333  *   0 on success and stats is filled, negative errno value otherwise and
334  *   rte_errno is set.
335  */
336 int
337 mlx5_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
338 {
339         struct priv *priv = dev->data->dev_private;
340         struct rte_eth_stats tmp = {0};
341         unsigned int i;
342         unsigned int idx;
343
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         return 0;
390 }
391
392 /**
393  * DPDK callback to clear device statistics.
394  *
395  * @param dev
396  *   Pointer to Ethernet device structure.
397  */
398 void
399 mlx5_stats_reset(struct rte_eth_dev *dev)
400 {
401         struct priv *priv = dev->data->dev_private;
402         unsigned int i;
403         unsigned int idx;
404
405         for (i = 0; (i != priv->rxqs_n); ++i) {
406                 if ((*priv->rxqs)[i] == NULL)
407                         continue;
408                 idx = (*priv->rxqs)[i]->stats.idx;
409                 (*priv->rxqs)[i]->stats =
410                         (struct mlx5_rxq_stats){ .idx = idx };
411         }
412         for (i = 0; (i != priv->txqs_n); ++i) {
413                 if ((*priv->txqs)[i] == NULL)
414                         continue;
415                 idx = (*priv->txqs)[i]->stats.idx;
416                 (*priv->txqs)[i]->stats =
417                         (struct mlx5_txq_stats){ .idx = idx };
418         }
419 #ifndef MLX5_PMD_SOFT_COUNTERS
420         /* FIXME: reset hardware counters. */
421 #endif
422 }
423
424 /**
425  * DPDK callback to clear device extended statistics.
426  *
427  * @param dev
428  *   Pointer to Ethernet device structure.
429  */
430 void
431 mlx5_xstats_reset(struct rte_eth_dev *dev)
432 {
433         struct priv *priv = dev->data->dev_private;
434         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
435         int stats_n;
436         unsigned int i;
437         unsigned int n = xstats_ctrl->mlx5_stats_n;
438         uint64_t counters[n];
439         int ret;
440
441         stats_n = mlx5_ethtool_get_stats_n(dev);
442         if (stats_n < 0) {
443                 DRV_LOG(ERR, "port %u cannot get stats: %s", dev->data->port_id,
444                         strerror(-stats_n));
445                 return;
446         }
447         if (xstats_ctrl->stats_n != stats_n)
448                 mlx5_xstats_init(dev);
449         ret = mlx5_read_dev_counters(dev, counters);
450         if (ret) {
451                 DRV_LOG(ERR, "port %u cannot read device counters: %s",
452                         dev->data->port_id, strerror(rte_errno));
453                 return;
454         }
455         for (i = 0; i != n; ++i)
456                 xstats_ctrl->base[i] = counters[i];
457 }
458
459 /**
460  * DPDK callback to retrieve names of extended device statistics
461  *
462  * @param dev
463  *   Pointer to Ethernet device structure.
464  * @param[out] xstats_names
465  *   Buffer to insert names into.
466  * @param n
467  *   Number of names.
468  *
469  * @return
470  *   Number of xstats names.
471  */
472 int
473 mlx5_xstats_get_names(struct rte_eth_dev *dev __rte_unused,
474                       struct rte_eth_xstat_name *xstats_names, unsigned int n)
475 {
476         unsigned int i;
477         struct priv *priv = dev->data->dev_private;
478         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
479         unsigned int mlx5_xstats_n = xstats_ctrl->mlx5_stats_n;
480
481         if (n >= mlx5_xstats_n && xstats_names) {
482                 for (i = 0; i != mlx5_xstats_n; ++i) {
483                         strncpy(xstats_names[i].name,
484                                 xstats_ctrl->info[i].dpdk_name,
485                                 RTE_ETH_XSTATS_NAME_SIZE);
486                         xstats_names[i].name[RTE_ETH_XSTATS_NAME_SIZE - 1] = 0;
487                 }
488         }
489         return mlx5_xstats_n;
490 }