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