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