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