net/mlx5: add out of buffer counter to extended statistic
[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  * Init the structures to read device counters.
180  *
181  * @param priv
182  *   Pointer to private structure.
183  */
184 void
185 priv_xstats_init(struct priv *priv)
186 {
187         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
188         unsigned int i;
189         unsigned int j;
190         char ifname[IF_NAMESIZE];
191         struct ifreq ifr;
192         struct ethtool_drvinfo drvinfo;
193         struct ethtool_gstrings *strings = NULL;
194         unsigned int dev_stats_n;
195         unsigned int str_sz;
196
197         if (priv_get_ifname(priv, &ifname)) {
198                 WARN("unable to get interface name");
199                 return;
200         }
201         /* How many statistics are available. */
202         drvinfo.cmd = ETHTOOL_GDRVINFO;
203         ifr.ifr_data = (caddr_t)&drvinfo;
204         if (priv_ifreq(priv, SIOCETHTOOL, &ifr) != 0) {
205                 WARN("unable to get driver info");
206                 return;
207         }
208         dev_stats_n = drvinfo.n_stats;
209         if (dev_stats_n < 1) {
210                 WARN("no extended statistics available");
211                 return;
212         }
213         xstats_ctrl->stats_n = dev_stats_n;
214         /* Allocate memory to grab stat names and values. */
215         str_sz = dev_stats_n * ETH_GSTRING_LEN;
216         strings = (struct ethtool_gstrings *)
217                   rte_malloc("xstats_strings",
218                              str_sz + sizeof(struct ethtool_gstrings), 0);
219         if (!strings) {
220                 WARN("unable to allocate memory for xstats");
221                 return;
222         }
223         strings->cmd = ETHTOOL_GSTRINGS;
224         strings->string_set = ETH_SS_STATS;
225         strings->len = dev_stats_n;
226         ifr.ifr_data = (caddr_t)strings;
227         if (priv_ifreq(priv, SIOCETHTOOL, &ifr) != 0) {
228                 WARN("unable to get statistic names");
229                 goto free;
230         }
231         for (j = 0; j != xstats_n; ++j)
232                 xstats_ctrl->dev_table_idx[j] = dev_stats_n;
233         for (i = 0; i != dev_stats_n; ++i) {
234                 const char *curr_string = (const char *)
235                         &strings->data[i * ETH_GSTRING_LEN];
236
237                 for (j = 0; j != xstats_n; ++j) {
238                         if (!strcmp(mlx5_counters_init[j].ctr_name,
239                                     curr_string)) {
240                                 xstats_ctrl->dev_table_idx[j] = i;
241                                 break;
242                         }
243                 }
244         }
245         for (j = 0; j != xstats_n; ++j) {
246                 if (priv_is_ib_cntr(mlx5_counters_init[i].ctr_name))
247                         continue;
248                 if (xstats_ctrl->dev_table_idx[j] >= dev_stats_n) {
249                         WARN("counter \"%s\" is not recognized",
250                              mlx5_counters_init[j].dpdk_name);
251                         goto free;
252                 }
253         }
254         /* Copy to base at first time. */
255         assert(xstats_n <= MLX5_MAX_XSTATS);
256         priv_read_dev_counters(priv, xstats_ctrl->base);
257 free:
258         rte_free(strings);
259 }
260
261 /**
262  * Get device extended statistics.
263  *
264  * @param priv
265  *   Pointer to private structure.
266  * @param[out] stats
267  *   Pointer to rte extended stats table.
268  *
269  * @return
270  *   Number of extended stats on success and stats is filled,
271  *   negative on error.
272  */
273 static int
274 priv_xstats_get(struct priv *priv, struct rte_eth_xstat *stats)
275 {
276         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
277         unsigned int i;
278         unsigned int n = xstats_n;
279         uint64_t counters[n];
280
281         if (priv_read_dev_counters(priv, counters) < 0)
282                 return -1;
283         for (i = 0; i != xstats_n; ++i) {
284                 stats[i].id = i;
285                 stats[i].value = (counters[i] - xstats_ctrl->base[i]);
286         }
287         return n;
288 }
289
290 /**
291  * Reset device extended statistics.
292  *
293  * @param priv
294  *   Pointer to private structure.
295  */
296 static void
297 priv_xstats_reset(struct priv *priv)
298 {
299         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
300         unsigned int i;
301         unsigned int n = xstats_n;
302         uint64_t counters[n];
303
304         if (priv_read_dev_counters(priv, counters) < 0)
305                 return;
306         for (i = 0; i != n; ++i)
307                 xstats_ctrl->base[i] = counters[i];
308 }
309
310 /**
311  * DPDK callback to get device statistics.
312  *
313  * @param dev
314  *   Pointer to Ethernet device structure.
315  * @param[out] stats
316  *   Stats structure output buffer.
317  */
318 void
319 mlx5_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
320 {
321         struct priv *priv = mlx5_get_priv(dev);
322         struct rte_eth_stats tmp = {0};
323         unsigned int i;
324         unsigned int idx;
325
326         priv_lock(priv);
327         /* Add software counters. */
328         for (i = 0; (i != priv->rxqs_n); ++i) {
329                 struct rxq *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 txq *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.odropped;
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.odropped;
367         }
368 #ifndef MLX5_PMD_SOFT_COUNTERS
369         /* FIXME: retrieve and add hardware counters. */
370 #endif
371         *stats = tmp;
372         priv_unlock(priv);
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         priv_lock(priv);
389         for (i = 0; (i != priv->rxqs_n); ++i) {
390                 if ((*priv->rxqs)[i] == NULL)
391                         continue;
392                 idx = (*priv->rxqs)[i]->stats.idx;
393                 (*priv->rxqs)[i]->stats =
394                         (struct mlx5_rxq_stats){ .idx = idx };
395         }
396         for (i = 0; (i != priv->txqs_n); ++i) {
397                 if ((*priv->txqs)[i] == NULL)
398                         continue;
399                 idx = (*priv->txqs)[i]->stats.idx;
400                 (*priv->txqs)[i]->stats =
401                         (struct mlx5_txq_stats){ .idx = idx };
402         }
403 #ifndef MLX5_PMD_SOFT_COUNTERS
404         /* FIXME: reset hardware counters. */
405 #endif
406         priv_unlock(priv);
407 }
408
409 /**
410  * DPDK callback to get extended device statistics.
411  *
412  * @param dev
413  *   Pointer to Ethernet device structure.
414  * @param[out] stats
415  *   Stats table output buffer.
416  * @param n
417  *   The size of the stats table.
418  *
419  * @return
420  *   Number of xstats on success, negative on failure.
421  */
422 int
423 mlx5_xstats_get(struct rte_eth_dev *dev,
424                 struct rte_eth_xstat *stats, unsigned int n)
425 {
426         struct priv *priv = mlx5_get_priv(dev);
427         int ret = xstats_n;
428
429         if (n >= xstats_n && stats) {
430                 priv_lock(priv);
431                 ret = priv_xstats_get(priv, stats);
432                 priv_unlock(priv);
433         }
434         return ret;
435 }
436
437 /**
438  * DPDK callback to clear device extended statistics.
439  *
440  * @param dev
441  *   Pointer to Ethernet device structure.
442  */
443 void
444 mlx5_xstats_reset(struct rte_eth_dev *dev)
445 {
446         struct priv *priv = mlx5_get_priv(dev);
447
448         priv_lock(priv);
449         priv_xstats_reset(priv);
450         priv_unlock(priv);
451 }
452
453 /**
454  * DPDK callback to retrieve names of extended device statistics
455  *
456  * @param dev
457  *   Pointer to Ethernet device structure.
458  * @param[out] xstats_names
459  *   Buffer to insert names into.
460  * @param n
461  *   Number of names.
462  *
463  * @return
464  *   Number of xstats names.
465  */
466 int
467 mlx5_xstats_get_names(struct rte_eth_dev *dev,
468                 struct rte_eth_xstat_name *xstats_names, unsigned int n)
469 {
470         struct priv *priv = mlx5_get_priv(dev);
471         unsigned int i;
472
473         if (n >= xstats_n && xstats_names) {
474                 priv_lock(priv);
475                 for (i = 0; i != xstats_n; ++i) {
476                         strncpy(xstats_names[i].name,
477                                 mlx5_counters_init[i].dpdk_name,
478                                 RTE_ETH_XSTATS_NAME_SIZE);
479                         xstats_names[i].name[RTE_ETH_XSTATS_NAME_SIZE - 1] = 0;
480                 }
481                 priv_unlock(priv);
482         }
483         return xstats_n;
484 }