New upstream version 17.11.5
[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;
360         unsigned int i;
361         unsigned int idx;
362
363         memset(&tmp, 0, sizeof(tmp));
364         /* Add software counters. */
365         for (i = 0; (i != priv->rxqs_n); ++i) {
366                 struct mlx5_rxq_data *rxq = (*priv->rxqs)[i];
367
368                 if (rxq == NULL)
369                         continue;
370                 idx = rxq->stats.idx;
371                 if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
372 #ifdef MLX5_PMD_SOFT_COUNTERS
373                         tmp.q_ipackets[idx] += rxq->stats.ipackets;
374                         tmp.q_ibytes[idx] += rxq->stats.ibytes;
375 #endif
376                         tmp.q_errors[idx] += (rxq->stats.idropped +
377                                               rxq->stats.rx_nombuf);
378                 }
379 #ifdef MLX5_PMD_SOFT_COUNTERS
380                 tmp.ipackets += rxq->stats.ipackets;
381                 tmp.ibytes += rxq->stats.ibytes;
382 #endif
383                 tmp.ierrors += rxq->stats.idropped;
384                 tmp.rx_nombuf += rxq->stats.rx_nombuf;
385         }
386         for (i = 0; (i != priv->txqs_n); ++i) {
387                 struct mlx5_txq_data *txq = (*priv->txqs)[i];
388
389                 if (txq == NULL)
390                         continue;
391                 idx = txq->stats.idx;
392                 if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
393 #ifdef MLX5_PMD_SOFT_COUNTERS
394                         tmp.q_opackets[idx] += txq->stats.opackets;
395                         tmp.q_obytes[idx] += txq->stats.obytes;
396 #endif
397                         tmp.q_errors[idx] += txq->stats.oerrors;
398                 }
399 #ifdef MLX5_PMD_SOFT_COUNTERS
400                 tmp.opackets += txq->stats.opackets;
401                 tmp.obytes += txq->stats.obytes;
402 #endif
403                 tmp.oerrors += txq->stats.oerrors;
404         }
405 #ifndef MLX5_PMD_SOFT_COUNTERS
406         /* FIXME: retrieve and add hardware counters. */
407 #endif
408         *stats = tmp;
409         return 0;
410 }
411
412 /**
413  * DPDK callback to clear device statistics.
414  *
415  * @param dev
416  *   Pointer to Ethernet device structure.
417  */
418 void
419 mlx5_stats_reset(struct rte_eth_dev *dev)
420 {
421         struct priv *priv = dev->data->dev_private;
422         unsigned int i;
423         unsigned int idx;
424
425         for (i = 0; (i != priv->rxqs_n); ++i) {
426                 if ((*priv->rxqs)[i] == NULL)
427                         continue;
428                 idx = (*priv->rxqs)[i]->stats.idx;
429                 (*priv->rxqs)[i]->stats =
430                         (struct mlx5_rxq_stats){ .idx = idx };
431         }
432         for (i = 0; (i != priv->txqs_n); ++i) {
433                 if ((*priv->txqs)[i] == NULL)
434                         continue;
435                 idx = (*priv->txqs)[i]->stats.idx;
436                 (*priv->txqs)[i]->stats =
437                         (struct mlx5_txq_stats){ .idx = idx };
438         }
439 #ifndef MLX5_PMD_SOFT_COUNTERS
440         /* FIXME: reset hardware counters. */
441 #endif
442 }
443
444 /**
445  * DPDK callback to clear device extended statistics.
446  *
447  * @param dev
448  *   Pointer to Ethernet device structure.
449  */
450 void
451 mlx5_xstats_reset(struct rte_eth_dev *dev)
452 {
453         struct priv *priv = dev->data->dev_private;
454         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
455         int stats_n;
456         unsigned int i;
457         unsigned int n = xstats_n;
458         uint64_t counters[n];
459         int ret;
460
461         stats_n = mlx5_ethtool_get_stats_n(dev);
462         if (stats_n < 0) {
463                 DRV_LOG(ERR, "port %u cannot get stats: %s", dev->data->port_id,
464                         strerror(-stats_n));
465                 return;
466         }
467         if (xstats_ctrl->stats_n != stats_n)
468                 mlx5_xstats_init(dev);
469         ret = mlx5_read_dev_counters(dev, counters);
470         if (ret) {
471                 DRV_LOG(ERR, "port %u cannot read device counters: %s",
472                         dev->data->port_id, strerror(rte_errno));
473                 return;
474         }
475         for (i = 0; i != n; ++i)
476                 xstats_ctrl->base[i] = counters[i];
477 }
478
479 /**
480  * DPDK callback to retrieve names of extended device statistics
481  *
482  * @param dev
483  *   Pointer to Ethernet device structure.
484  * @param[out] xstats_names
485  *   Buffer to insert names into.
486  * @param n
487  *   Number of names.
488  *
489  * @return
490  *   Number of xstats names.
491  */
492 int
493 mlx5_xstats_get_names(struct rte_eth_dev *dev __rte_unused,
494                       struct rte_eth_xstat_name *xstats_names, unsigned int n)
495 {
496         unsigned int i;
497
498         if (n >= xstats_n && xstats_names) {
499                 for (i = 0; i != xstats_n; ++i) {
500                         strncpy(xstats_names[i].name,
501                                 mlx5_counters_init[i].dpdk_name,
502                                 RTE_ETH_XSTATS_NAME_SIZE);
503                         xstats_names[i].name[RTE_ETH_XSTATS_NAME_SIZE - 1] = 0;
504                 }
505         }
506         return xstats_n;
507 }