0febed878ac4ad3dabd9755dae4814cd72d2b96d
[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 on error.
132  */
133 static int
134 mlx5_read_dev_counters(struct rte_eth_dev *dev, uint64_t *stats)
135 {
136         struct priv *priv = dev->data->dev_private;
137         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
138         unsigned int i;
139         struct ifreq ifr;
140         unsigned int stats_sz = xstats_ctrl->stats_n * sizeof(uint64_t);
141         unsigned char et_stat_buf[sizeof(struct ethtool_stats) + stats_sz];
142         struct ethtool_stats *et_stats = (struct ethtool_stats *)et_stat_buf;
143
144         et_stats->cmd = ETHTOOL_GSTATS;
145         et_stats->n_stats = xstats_ctrl->stats_n;
146         ifr.ifr_data = (caddr_t)et_stats;
147         if (mlx5_ifreq(dev, SIOCETHTOOL, &ifr) != 0) {
148                 WARN("unable to read statistic values from device");
149                 return -1;
150         }
151         for (i = 0; i != xstats_n; ++i) {
152                 if (mlx5_counters_init[i].ib) {
153                         FILE *file;
154                         MKSTR(path, "%s/ports/1/hw_counters/%s",
155                               priv->ibdev_path,
156                               mlx5_counters_init[i].ctr_name);
157
158                         file = fopen(path, "rb");
159                         if (file) {
160                                 int n = fscanf(file, "%" SCNu64, &stats[i]);
161
162                                 fclose(file);
163                                 if (n != 1)
164                                         stats[i] = 0;
165                         }
166                 } else {
167                         stats[i] = (uint64_t)
168                                 et_stats->data[xstats_ctrl->dev_table_idx[i]];
169                 }
170         }
171         return 0;
172 }
173
174 /**
175  * Query the number of statistics provided by ETHTOOL.
176  *
177  * @param dev
178  *   Pointer to Ethernet device.
179  *
180  * @return
181  *   Number of statistics on success, -1 on error.
182  */
183 static int
184 mlx5_ethtool_get_stats_n(struct rte_eth_dev *dev) {
185         struct ethtool_drvinfo drvinfo;
186         struct ifreq ifr;
187
188         drvinfo.cmd = ETHTOOL_GDRVINFO;
189         ifr.ifr_data = (caddr_t)&drvinfo;
190         if (mlx5_ifreq(dev, SIOCETHTOOL, &ifr) != 0) {
191                 WARN("unable to query number of statistics");
192                 return -1;
193         }
194         return drvinfo.n_stats;
195 }
196
197 /**
198  * Init the structures to read device counters.
199  *
200  * @param dev
201  *   Pointer to Ethernet device.
202  */
203 void
204 mlx5_xstats_init(struct rte_eth_dev *dev)
205 {
206         struct priv *priv = dev->data->dev_private;
207         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
208         unsigned int i;
209         unsigned int j;
210         struct ifreq ifr;
211         struct ethtool_gstrings *strings = NULL;
212         unsigned int dev_stats_n;
213         unsigned int str_sz;
214
215         dev_stats_n = mlx5_ethtool_get_stats_n(dev);
216         if (dev_stats_n < 1) {
217                 WARN("no extended statistics available");
218                 return;
219         }
220         xstats_ctrl->stats_n = dev_stats_n;
221         /* Allocate memory to grab stat names and values. */
222         str_sz = dev_stats_n * ETH_GSTRING_LEN;
223         strings = (struct ethtool_gstrings *)
224                   rte_malloc("xstats_strings",
225                              str_sz + sizeof(struct ethtool_gstrings), 0);
226         if (!strings) {
227                 WARN("unable to allocate memory for xstats");
228                 return;
229         }
230         strings->cmd = ETHTOOL_GSTRINGS;
231         strings->string_set = ETH_SS_STATS;
232         strings->len = dev_stats_n;
233         ifr.ifr_data = (caddr_t)strings;
234         if (mlx5_ifreq(dev, SIOCETHTOOL, &ifr) != 0) {
235                 WARN("unable to get statistic names");
236                 goto free;
237         }
238         for (j = 0; j != xstats_n; ++j)
239                 xstats_ctrl->dev_table_idx[j] = dev_stats_n;
240         for (i = 0; i != dev_stats_n; ++i) {
241                 const char *curr_string = (const char *)
242                         &strings->data[i * ETH_GSTRING_LEN];
243
244                 for (j = 0; j != xstats_n; ++j) {
245                         if (!strcmp(mlx5_counters_init[j].ctr_name,
246                                     curr_string)) {
247                                 xstats_ctrl->dev_table_idx[j] = i;
248                                 break;
249                         }
250                 }
251         }
252         for (j = 0; j != xstats_n; ++j) {
253                 if (mlx5_counters_init[j].ib)
254                         continue;
255                 if (xstats_ctrl->dev_table_idx[j] >= dev_stats_n) {
256                         WARN("counter \"%s\" is not recognized",
257                              mlx5_counters_init[j].dpdk_name);
258                         goto free;
259                 }
260         }
261         /* Copy to base at first time. */
262         assert(xstats_n <= MLX5_MAX_XSTATS);
263         mlx5_read_dev_counters(dev, xstats_ctrl->base);
264 free:
265         rte_free(strings);
266 }
267
268 /**
269  * DPDK callback to get extended device statistics.
270  *
271  * @param dev
272  *   Pointer to Ethernet device.
273  * @param[out] stats
274  *   Pointer to rte extended stats table.
275  * @param n
276  *   The size of the stats table.
277  *
278  * @return
279  *   Number of extended stats on success and stats is filled,
280  *   negative on error.
281  */
282 int
283 mlx5_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstat *stats,
284                 unsigned int n)
285 {
286         struct priv *priv = dev->data->dev_private;
287         unsigned int i;
288         uint64_t counters[n];
289         int ret = 0;
290
291         if (n >= xstats_n && stats) {
292                 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
293                 int stats_n;
294
295                 stats_n = mlx5_ethtool_get_stats_n(dev);
296                 if (stats_n < 0)
297                         return -1;
298                 if (xstats_ctrl->stats_n != stats_n)
299                         mlx5_xstats_init(dev);
300                 ret = mlx5_read_dev_counters(dev, counters);
301                 if (ret)
302                         return ret;
303                 for (i = 0; i != xstats_n; ++i) {
304                         stats[i].id = i;
305                         stats[i].value = (counters[i] - xstats_ctrl->base[i]);
306                 }
307         }
308         return n;
309 }
310
311 /**
312  * DPDK callback to get device statistics.
313  *
314  * @param dev
315  *   Pointer to Ethernet device structure.
316  * @param[out] stats
317  *   Stats structure output buffer.
318  */
319 int
320 mlx5_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
321 {
322         struct priv *priv = dev->data->dev_private;
323         struct rte_eth_stats tmp = {0};
324         unsigned int i;
325         unsigned int idx;
326
327         /* Add software counters. */
328         for (i = 0; (i != priv->rxqs_n); ++i) {
329                 struct mlx5_rxq_data *rxq = (*priv->rxqs)[i];
330
331                 if (rxq == NULL)
332                         continue;
333                 idx = rxq->stats.idx;
334                 if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
335 #ifdef MLX5_PMD_SOFT_COUNTERS
336                         tmp.q_ipackets[idx] += rxq->stats.ipackets;
337                         tmp.q_ibytes[idx] += rxq->stats.ibytes;
338 #endif
339                         tmp.q_errors[idx] += (rxq->stats.idropped +
340                                               rxq->stats.rx_nombuf);
341                 }
342 #ifdef MLX5_PMD_SOFT_COUNTERS
343                 tmp.ipackets += rxq->stats.ipackets;
344                 tmp.ibytes += rxq->stats.ibytes;
345 #endif
346                 tmp.ierrors += rxq->stats.idropped;
347                 tmp.rx_nombuf += rxq->stats.rx_nombuf;
348         }
349         for (i = 0; (i != priv->txqs_n); ++i) {
350                 struct mlx5_txq_data *txq = (*priv->txqs)[i];
351
352                 if (txq == NULL)
353                         continue;
354                 idx = txq->stats.idx;
355                 if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
356 #ifdef MLX5_PMD_SOFT_COUNTERS
357                         tmp.q_opackets[idx] += txq->stats.opackets;
358                         tmp.q_obytes[idx] += txq->stats.obytes;
359 #endif
360                         tmp.q_errors[idx] += txq->stats.oerrors;
361                 }
362 #ifdef MLX5_PMD_SOFT_COUNTERS
363                 tmp.opackets += txq->stats.opackets;
364                 tmp.obytes += txq->stats.obytes;
365 #endif
366                 tmp.oerrors += txq->stats.oerrors;
367         }
368 #ifndef MLX5_PMD_SOFT_COUNTERS
369         /* FIXME: retrieve and add hardware counters. */
370 #endif
371         *stats = tmp;
372         return 0;
373 }
374
375 /**
376  * DPDK callback to clear device statistics.
377  *
378  * @param dev
379  *   Pointer to Ethernet device structure.
380  */
381 void
382 mlx5_stats_reset(struct rte_eth_dev *dev)
383 {
384         struct priv *priv = dev->data->dev_private;
385         unsigned int i;
386         unsigned int idx;
387
388         for (i = 0; (i != priv->rxqs_n); ++i) {
389                 if ((*priv->rxqs)[i] == NULL)
390                         continue;
391                 idx = (*priv->rxqs)[i]->stats.idx;
392                 (*priv->rxqs)[i]->stats =
393                         (struct mlx5_rxq_stats){ .idx = idx };
394         }
395         for (i = 0; (i != priv->txqs_n); ++i) {
396                 if ((*priv->txqs)[i] == NULL)
397                         continue;
398                 idx = (*priv->txqs)[i]->stats.idx;
399                 (*priv->txqs)[i]->stats =
400                         (struct mlx5_txq_stats){ .idx = idx };
401         }
402 #ifndef MLX5_PMD_SOFT_COUNTERS
403         /* FIXME: reset hardware counters. */
404 #endif
405 }
406
407 /**
408  * DPDK callback to clear device extended statistics.
409  *
410  * @param dev
411  *   Pointer to Ethernet device structure.
412  */
413 void
414 mlx5_xstats_reset(struct rte_eth_dev *dev)
415 {
416         struct priv *priv = dev->data->dev_private;
417         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
418         int stats_n;
419         unsigned int i;
420         unsigned int n = xstats_n;
421         uint64_t counters[n];
422
423         stats_n = mlx5_ethtool_get_stats_n(dev);
424         if (stats_n < 0)
425                 return;
426         if (xstats_ctrl->stats_n != stats_n)
427                 mlx5_xstats_init(dev);
428         if (mlx5_read_dev_counters(dev, counters) < 0)
429                 return;
430         for (i = 0; i != n; ++i)
431                 xstats_ctrl->base[i] = counters[i];
432 }
433
434 /**
435  * DPDK callback to retrieve names of extended device statistics
436  *
437  * @param dev
438  *   Pointer to Ethernet device structure.
439  * @param[out] xstats_names
440  *   Buffer to insert names into.
441  * @param n
442  *   Number of names.
443  *
444  * @return
445  *   Number of xstats names.
446  */
447 int
448 mlx5_xstats_get_names(struct rte_eth_dev *dev __rte_unused,
449                 struct rte_eth_xstat_name *xstats_names, unsigned int n)
450 {
451         unsigned int i;
452
453         if (n >= xstats_n && xstats_names) {
454                 for (i = 0; i != xstats_n; ++i) {
455                         strncpy(xstats_names[i].name,
456                                 mlx5_counters_init[i].dpdk_name,
457                                 RTE_ETH_XSTATS_NAME_SIZE);
458                         xstats_names[i].name[RTE_ETH_XSTATS_NAME_SIZE - 1] = 0;
459                 }
460         }
461         return xstats_n;
462 }