New upstream version 17.11.3
[deb_dpdk.git] / drivers / net / mlx5 / mlx5_stats.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright 2015 6WIND S.A.
5  *   Copyright 2015 Mellanox.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of 6WIND S.A. nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <inttypes.h>
35 #include <linux/sockios.h>
36 #include <linux/ethtool.h>
37 #include <stdint.h>
38 #include <stdio.h>
39
40 #include <rte_ethdev.h>
41 #include <rte_common.h>
42 #include <rte_malloc.h>
43
44 #include "mlx5.h"
45 #include "mlx5_rxtx.h"
46 #include "mlx5_defs.h"
47
48 struct mlx5_counter_ctrl {
49         /* Name of the counter. */
50         char dpdk_name[RTE_ETH_XSTATS_NAME_SIZE];
51         /* Name of the counter on the device table. */
52         char ctr_name[RTE_ETH_XSTATS_NAME_SIZE];
53         uint32_t ib:1; /**< Nonzero for IB counters. */
54 };
55
56 static const struct mlx5_counter_ctrl mlx5_counters_init[] = {
57         {
58                 .dpdk_name = "rx_port_unicast_bytes",
59                 .ctr_name = "rx_vport_unicast_bytes",
60         },
61         {
62                 .dpdk_name = "rx_port_multicast_bytes",
63                 .ctr_name = "rx_vport_multicast_bytes",
64         },
65         {
66                 .dpdk_name = "rx_port_broadcast_bytes",
67                 .ctr_name = "rx_vport_broadcast_bytes",
68         },
69         {
70                 .dpdk_name = "rx_port_unicast_packets",
71                 .ctr_name = "rx_vport_unicast_packets",
72         },
73         {
74                 .dpdk_name = "rx_port_multicast_packets",
75                 .ctr_name = "rx_vport_multicast_packets",
76         },
77         {
78                 .dpdk_name = "rx_port_broadcast_packets",
79                 .ctr_name = "rx_vport_broadcast_packets",
80         },
81         {
82                 .dpdk_name = "tx_port_unicast_bytes",
83                 .ctr_name = "tx_vport_unicast_bytes",
84         },
85         {
86                 .dpdk_name = "tx_port_multicast_bytes",
87                 .ctr_name = "tx_vport_multicast_bytes",
88         },
89         {
90                 .dpdk_name = "tx_port_broadcast_bytes",
91                 .ctr_name = "tx_vport_broadcast_bytes",
92         },
93         {
94                 .dpdk_name = "tx_port_unicast_packets",
95                 .ctr_name = "tx_vport_unicast_packets",
96         },
97         {
98                 .dpdk_name = "tx_port_multicast_packets",
99                 .ctr_name = "tx_vport_multicast_packets",
100         },
101         {
102                 .dpdk_name = "tx_port_broadcast_packets",
103                 .ctr_name = "tx_vport_broadcast_packets",
104         },
105         {
106                 .dpdk_name = "rx_wqe_err",
107                 .ctr_name = "rx_wqe_err",
108         },
109         {
110                 .dpdk_name = "rx_crc_errors_phy",
111                 .ctr_name = "rx_crc_errors_phy",
112         },
113         {
114                 .dpdk_name = "rx_in_range_len_errors_phy",
115                 .ctr_name = "rx_in_range_len_errors_phy",
116         },
117         {
118                 .dpdk_name = "rx_symbol_err_phy",
119                 .ctr_name = "rx_symbol_err_phy",
120         },
121         {
122                 .dpdk_name = "tx_errors_phy",
123                 .ctr_name = "tx_errors_phy",
124         },
125         {
126                 .dpdk_name = "rx_out_of_buffer",
127                 .ctr_name = "out_of_buffer",
128                 .ib = 1,
129         },
130 };
131
132 static const unsigned int xstats_n = RTE_DIM(mlx5_counters_init);
133
134 /**
135  * Read device counters table.
136  *
137  * @param dev
138  *   Pointer to Ethernet device.
139  * @param[out] stats
140  *   Counters table output buffer.
141  *
142  * @return
143  *   0 on success and stats is filled, negative errno value otherwise and
144  *   rte_errno is set.
145  */
146 static int
147 mlx5_read_dev_counters(struct rte_eth_dev *dev, uint64_t *stats)
148 {
149         struct priv *priv = dev->data->dev_private;
150         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
151         unsigned int i;
152         struct ifreq ifr;
153         unsigned int stats_sz = xstats_ctrl->stats_n * sizeof(uint64_t);
154         unsigned char et_stat_buf[sizeof(struct ethtool_stats) + stats_sz];
155         struct ethtool_stats *et_stats = (struct ethtool_stats *)et_stat_buf;
156         int ret;
157
158         et_stats->cmd = ETHTOOL_GSTATS;
159         et_stats->n_stats = xstats_ctrl->stats_n;
160         ifr.ifr_data = (caddr_t)et_stats;
161         ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
162         if (ret) {
163                 DRV_LOG(WARNING,
164                         "port %u unable to read statistic values from device",
165                         dev->data->port_id);
166                 return ret;
167         }
168         for (i = 0; i != xstats_n; ++i) {
169                 if (mlx5_counters_init[i].ib) {
170                         FILE *file;
171                         MKSTR(path, "%s/ports/1/hw_counters/%s",
172                               priv->ibdev_path,
173                               mlx5_counters_init[i].ctr_name);
174
175                         file = fopen(path, "rb");
176                         if (file) {
177                                 int n = fscanf(file, "%" SCNu64, &stats[i]);
178
179                                 fclose(file);
180                                 if (n != 1)
181                                         stats[i] = 0;
182                         }
183                 } else {
184                         stats[i] = (uint64_t)
185                                 et_stats->data[xstats_ctrl->dev_table_idx[i]];
186                 }
187         }
188         return 0;
189 }
190
191 /**
192  * Query the number of statistics provided by ETHTOOL.
193  *
194  * @param dev
195  *   Pointer to Ethernet device.
196  *
197  * @return
198  *   Number of statistics on success, negative errno value otherwise and
199  *   rte_errno is set.
200  */
201 static int
202 mlx5_ethtool_get_stats_n(struct rte_eth_dev *dev) {
203         struct ethtool_drvinfo drvinfo;
204         struct ifreq ifr;
205         int ret;
206
207         drvinfo.cmd = ETHTOOL_GDRVINFO;
208         ifr.ifr_data = (caddr_t)&drvinfo;
209         ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
210         if (ret) {
211                 DRV_LOG(WARNING, "port %u unable to query number of statistics",
212                         dev->data->port_id);
213                 return ret;
214         }
215         return drvinfo.n_stats;
216 }
217
218 /**
219  * Init the structures to read device counters.
220  *
221  * @param dev
222  *   Pointer to Ethernet device.
223  */
224 void
225 mlx5_xstats_init(struct rte_eth_dev *dev)
226 {
227         struct priv *priv = dev->data->dev_private;
228         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
229         unsigned int i;
230         unsigned int j;
231         struct ifreq ifr;
232         struct ethtool_gstrings *strings = NULL;
233         unsigned int dev_stats_n;
234         unsigned int str_sz;
235         int ret;
236
237         ret = mlx5_ethtool_get_stats_n(dev);
238         if (ret < 0) {
239                 DRV_LOG(WARNING, "port %u no extended statistics available",
240                         dev->data->port_id);
241                 return;
242         }
243         dev_stats_n = ret;
244         xstats_ctrl->stats_n = dev_stats_n;
245         /* Allocate memory to grab stat names and values. */
246         str_sz = dev_stats_n * ETH_GSTRING_LEN;
247         strings = (struct ethtool_gstrings *)
248                   rte_malloc("xstats_strings",
249                              str_sz + sizeof(struct ethtool_gstrings), 0);
250         if (!strings) {
251                 DRV_LOG(WARNING, "port %u unable to allocate memory for xstats",
252                      dev->data->port_id);
253                 return;
254         }
255         strings->cmd = ETHTOOL_GSTRINGS;
256         strings->string_set = ETH_SS_STATS;
257         strings->len = dev_stats_n;
258         ifr.ifr_data = (caddr_t)strings;
259         ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
260         if (ret) {
261                 DRV_LOG(WARNING, "port %u unable to get statistic names",
262                         dev->data->port_id);
263                 goto free;
264         }
265         for (j = 0; j != xstats_n; ++j)
266                 xstats_ctrl->dev_table_idx[j] = dev_stats_n;
267         for (i = 0; i != dev_stats_n; ++i) {
268                 const char *curr_string = (const char *)
269                         &strings->data[i * ETH_GSTRING_LEN];
270
271                 for (j = 0; j != xstats_n; ++j) {
272                         if (!strcmp(mlx5_counters_init[j].ctr_name,
273                                     curr_string)) {
274                                 xstats_ctrl->dev_table_idx[j] = i;
275                                 break;
276                         }
277                 }
278         }
279         for (j = 0; j != xstats_n; ++j) {
280                 if (mlx5_counters_init[j].ib)
281                         continue;
282                 if (xstats_ctrl->dev_table_idx[j] >= dev_stats_n) {
283                         DRV_LOG(WARNING,
284                                 "port %u counter \"%s\" is not recognized",
285                                 dev->data->port_id,
286                                 mlx5_counters_init[j].dpdk_name);
287                         goto free;
288                 }
289         }
290         /* Copy to base at first time. */
291         assert(xstats_n <= MLX5_MAX_XSTATS);
292         ret = mlx5_read_dev_counters(dev, xstats_ctrl->base);
293         if (ret)
294                 DRV_LOG(ERR, "port %u cannot read device counters: %s",
295                         dev->data->port_id, strerror(rte_errno));
296 free:
297         rte_free(strings);
298 }
299
300 /**
301  * DPDK callback to get extended device statistics.
302  *
303  * @param dev
304  *   Pointer to Ethernet device.
305  * @param[out] stats
306  *   Pointer to rte extended stats table.
307  * @param n
308  *   The size of the stats table.
309  *
310  * @return
311  *   Number of extended stats on success and stats is filled,
312  *   negative on error and rte_errno is set.
313  */
314 int
315 mlx5_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstat *stats,
316                 unsigned int n)
317 {
318         struct priv *priv = dev->data->dev_private;
319         unsigned int i;
320         uint64_t counters[n];
321
322         if (n >= xstats_n && stats) {
323                 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
324                 int stats_n;
325                 int ret;
326
327                 stats_n = mlx5_ethtool_get_stats_n(dev);
328                 if (stats_n < 0)
329                         return stats_n;
330                 if (xstats_ctrl->stats_n != stats_n)
331                         mlx5_xstats_init(dev);
332                 ret = mlx5_read_dev_counters(dev, counters);
333                 if (ret)
334                         return ret;
335                 for (i = 0; i != xstats_n; ++i) {
336                         stats[i].id = i;
337                         stats[i].value = (counters[i] - xstats_ctrl->base[i]);
338                 }
339         }
340         return xstats_n;
341 }
342
343 /**
344  * DPDK callback to get device statistics.
345  *
346  * @param dev
347  *   Pointer to Ethernet device structure.
348  * @param[out] stats
349  *   Stats structure output buffer.
350  *
351  * @return
352  *   0 on success and stats is filled, negative errno value otherwise and
353  *   rte_errno is set.
354  */
355 int
356 mlx5_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
357 {
358         struct priv *priv = dev->data->dev_private;
359         struct rte_eth_stats tmp = {0};
360         unsigned int i;
361         unsigned int idx;
362
363         /* Add software counters. */
364         for (i = 0; (i != priv->rxqs_n); ++i) {
365                 struct mlx5_rxq_data *rxq = (*priv->rxqs)[i];
366
367                 if (rxq == NULL)
368                         continue;
369                 idx = rxq->stats.idx;
370                 if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
371 #ifdef MLX5_PMD_SOFT_COUNTERS
372                         tmp.q_ipackets[idx] += rxq->stats.ipackets;
373                         tmp.q_ibytes[idx] += rxq->stats.ibytes;
374 #endif
375                         tmp.q_errors[idx] += (rxq->stats.idropped +
376                                               rxq->stats.rx_nombuf);
377                 }
378 #ifdef MLX5_PMD_SOFT_COUNTERS
379                 tmp.ipackets += rxq->stats.ipackets;
380                 tmp.ibytes += rxq->stats.ibytes;
381 #endif
382                 tmp.ierrors += rxq->stats.idropped;
383                 tmp.rx_nombuf += rxq->stats.rx_nombuf;
384         }
385         for (i = 0; (i != priv->txqs_n); ++i) {
386                 struct mlx5_txq_data *txq = (*priv->txqs)[i];
387
388                 if (txq == NULL)
389                         continue;
390                 idx = txq->stats.idx;
391                 if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
392 #ifdef MLX5_PMD_SOFT_COUNTERS
393                         tmp.q_opackets[idx] += txq->stats.opackets;
394                         tmp.q_obytes[idx] += txq->stats.obytes;
395 #endif
396                         tmp.q_errors[idx] += txq->stats.oerrors;
397                 }
398 #ifdef MLX5_PMD_SOFT_COUNTERS
399                 tmp.opackets += txq->stats.opackets;
400                 tmp.obytes += txq->stats.obytes;
401 #endif
402                 tmp.oerrors += txq->stats.oerrors;
403         }
404 #ifndef MLX5_PMD_SOFT_COUNTERS
405         /* FIXME: retrieve and add hardware counters. */
406 #endif
407         *stats = tmp;
408         return 0;
409 }
410
411 /**
412  * DPDK callback to clear device statistics.
413  *
414  * @param dev
415  *   Pointer to Ethernet device structure.
416  */
417 void
418 mlx5_stats_reset(struct rte_eth_dev *dev)
419 {
420         struct priv *priv = dev->data->dev_private;
421         unsigned int i;
422         unsigned int idx;
423
424         for (i = 0; (i != priv->rxqs_n); ++i) {
425                 if ((*priv->rxqs)[i] == NULL)
426                         continue;
427                 idx = (*priv->rxqs)[i]->stats.idx;
428                 (*priv->rxqs)[i]->stats =
429                         (struct mlx5_rxq_stats){ .idx = idx };
430         }
431         for (i = 0; (i != priv->txqs_n); ++i) {
432                 if ((*priv->txqs)[i] == NULL)
433                         continue;
434                 idx = (*priv->txqs)[i]->stats.idx;
435                 (*priv->txqs)[i]->stats =
436                         (struct mlx5_txq_stats){ .idx = idx };
437         }
438 #ifndef MLX5_PMD_SOFT_COUNTERS
439         /* FIXME: reset hardware counters. */
440 #endif
441 }
442
443 /**
444  * DPDK callback to clear device extended statistics.
445  *
446  * @param dev
447  *   Pointer to Ethernet device structure.
448  */
449 void
450 mlx5_xstats_reset(struct rte_eth_dev *dev)
451 {
452         struct priv *priv = dev->data->dev_private;
453         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
454         int stats_n;
455         unsigned int i;
456         unsigned int n = xstats_n;
457         uint64_t counters[n];
458         int ret;
459
460         stats_n = mlx5_ethtool_get_stats_n(dev);
461         if (stats_n < 0) {
462                 DRV_LOG(ERR, "port %u cannot get stats: %s", dev->data->port_id,
463                         strerror(-stats_n));
464                 return;
465         }
466         if (xstats_ctrl->stats_n != stats_n)
467                 mlx5_xstats_init(dev);
468         ret = mlx5_read_dev_counters(dev, counters);
469         if (ret) {
470                 DRV_LOG(ERR, "port %u cannot read device counters: %s",
471                         dev->data->port_id, strerror(rte_errno));
472                 return;
473         }
474         for (i = 0; i != n; ++i)
475                 xstats_ctrl->base[i] = counters[i];
476 }
477
478 /**
479  * DPDK callback to retrieve names of extended device statistics
480  *
481  * @param dev
482  *   Pointer to Ethernet device structure.
483  * @param[out] xstats_names
484  *   Buffer to insert names into.
485  * @param n
486  *   Number of names.
487  *
488  * @return
489  *   Number of xstats names.
490  */
491 int
492 mlx5_xstats_get_names(struct rte_eth_dev *dev __rte_unused,
493                       struct rte_eth_xstat_name *xstats_names, unsigned int n)
494 {
495         unsigned int i;
496
497         if (n >= xstats_n && xstats_names) {
498                 for (i = 0; i != xstats_n; ++i) {
499                         strncpy(xstats_names[i].name,
500                                 mlx5_counters_init[i].dpdk_name,
501                                 RTE_ETH_XSTATS_NAME_SIZE);
502                         xstats_names[i].name[RTE_ETH_XSTATS_NAME_SIZE - 1] = 0;
503                 }
504         }
505         return xstats_n;
506 }