net/mlx5: support extended statistics
[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
130 static const unsigned int xstats_n = RTE_DIM(mlx5_counters_init);
131
132 /**
133  * Read device counters table.
134  *
135  * @param priv
136  *   Pointer to private structure.
137  * @param[out] stats
138  *   Counters table output buffer.
139  *
140  * @return
141  *   0 on success and stats is filled, negative on error.
142  */
143 static int
144 priv_read_dev_counters(struct priv *priv, uint64_t *stats)
145 {
146         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
147         unsigned int i;
148         struct ifreq ifr;
149         unsigned int stats_sz = (xstats_ctrl->stats_n * sizeof(uint64_t)) +
150                                  sizeof(struct ethtool_stats);
151         struct ethtool_stats et_stats[(stats_sz + (
152                                       sizeof(struct ethtool_stats) - 1)) /
153                                       sizeof(struct ethtool_stats)];
154
155         et_stats->cmd = ETHTOOL_GSTATS;
156         et_stats->n_stats = xstats_ctrl->stats_n;
157         ifr.ifr_data = (caddr_t)et_stats;
158         if (priv_ifreq(priv, SIOCETHTOOL, &ifr) != 0) {
159                 WARN("unable to read statistic values from device");
160                 return -1;
161         }
162         for (i = 0; i != xstats_n; ++i)
163                 stats[i] = (uint64_t)
164                            et_stats->data[xstats_ctrl->dev_table_idx[i]];
165         return 0;
166 }
167
168 /**
169  * Init the structures to read device counters.
170  *
171  * @param priv
172  *   Pointer to private structure.
173  */
174 void
175 priv_xstats_init(struct priv *priv)
176 {
177         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
178         unsigned int i;
179         unsigned int j;
180         char ifname[IF_NAMESIZE];
181         struct ifreq ifr;
182         struct ethtool_drvinfo drvinfo;
183         struct ethtool_gstrings *strings = NULL;
184         unsigned int dev_stats_n;
185         unsigned int str_sz;
186
187         if (priv_get_ifname(priv, &ifname)) {
188                 WARN("unable to get interface name");
189                 return;
190         }
191         /* How many statistics are available. */
192         drvinfo.cmd = ETHTOOL_GDRVINFO;
193         ifr.ifr_data = (caddr_t)&drvinfo;
194         if (priv_ifreq(priv, SIOCETHTOOL, &ifr) != 0) {
195                 WARN("unable to get driver info");
196                 return;
197         }
198         dev_stats_n = drvinfo.n_stats;
199         if (dev_stats_n < 1) {
200                 WARN("no extended statistics available");
201                 return;
202         }
203         xstats_ctrl->stats_n = dev_stats_n;
204         /* Allocate memory to grab stat names and values. */
205         str_sz = dev_stats_n * ETH_GSTRING_LEN;
206         strings = (struct ethtool_gstrings *)
207                   rte_malloc("xstats_strings",
208                              str_sz + sizeof(struct ethtool_gstrings), 0);
209         if (!strings) {
210                 WARN("unable to allocate memory for xstats");
211                 return;
212         }
213         strings->cmd = ETHTOOL_GSTRINGS;
214         strings->string_set = ETH_SS_STATS;
215         strings->len = dev_stats_n;
216         ifr.ifr_data = (caddr_t)strings;
217         if (priv_ifreq(priv, SIOCETHTOOL, &ifr) != 0) {
218                 WARN("unable to get statistic names");
219                 goto free;
220         }
221         for (j = 0; j != xstats_n; ++j)
222                 xstats_ctrl->dev_table_idx[j] = dev_stats_n;
223         for (i = 0; i != dev_stats_n; ++i) {
224                 const char *curr_string = (const char *)
225                         &strings->data[i * ETH_GSTRING_LEN];
226
227                 for (j = 0; j != xstats_n; ++j) {
228                         if (!strcmp(mlx5_counters_init[j].ctr_name,
229                                     curr_string)) {
230                                 xstats_ctrl->dev_table_idx[j] = i;
231                                 break;
232                         }
233                 }
234         }
235         for (j = 0; j != xstats_n; ++j) {
236                 if (xstats_ctrl->dev_table_idx[j] >= dev_stats_n) {
237                         WARN("counter \"%s\" is not recognized",
238                              mlx5_counters_init[j].dpdk_name);
239                         goto free;
240                 }
241         }
242         /* Copy to base at first time. */
243         assert(xstats_n <= MLX5_MAX_XSTATS);
244         priv_read_dev_counters(priv, xstats_ctrl->base);
245 free:
246         rte_free(strings);
247 }
248
249 /**
250  * Get device extended statistics.
251  *
252  * @param priv
253  *   Pointer to private structure.
254  * @param[out] stats
255  *   Pointer to rte extended stats table.
256  *
257  * @return
258  *   Number of extended stats on success and stats is filled,
259  *   negative on error.
260  */
261 static int
262 priv_xstats_get(struct priv *priv, struct rte_eth_xstat *stats)
263 {
264         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
265         unsigned int i;
266         unsigned int n = xstats_n;
267         uint64_t counters[n];
268
269         if (priv_read_dev_counters(priv, counters) < 0)
270                 return -1;
271         for (i = 0; i != xstats_n; ++i) {
272                 stats[i].id = i;
273                 stats[i].value = (counters[i] - xstats_ctrl->base[i]);
274         }
275         return n;
276 }
277
278 /**
279  * Reset device extended statistics.
280  *
281  * @param priv
282  *   Pointer to private structure.
283  */
284 static void
285 priv_xstats_reset(struct priv *priv)
286 {
287         struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
288         unsigned int i;
289         unsigned int n = xstats_n;
290         uint64_t counters[n];
291
292         if (priv_read_dev_counters(priv, counters) < 0)
293                 return;
294         for (i = 0; i != n; ++i)
295                 xstats_ctrl->base[i] = counters[i];
296 }
297
298 /**
299  * DPDK callback to get device statistics.
300  *
301  * @param dev
302  *   Pointer to Ethernet device structure.
303  * @param[out] stats
304  *   Stats structure output buffer.
305  */
306 void
307 mlx5_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
308 {
309         struct priv *priv = mlx5_get_priv(dev);
310         struct rte_eth_stats tmp = {0};
311         unsigned int i;
312         unsigned int idx;
313
314         priv_lock(priv);
315         /* Add software counters. */
316         for (i = 0; (i != priv->rxqs_n); ++i) {
317                 struct rxq *rxq = (*priv->rxqs)[i];
318
319                 if (rxq == NULL)
320                         continue;
321                 idx = rxq->stats.idx;
322                 if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
323 #ifdef MLX5_PMD_SOFT_COUNTERS
324                         tmp.q_ipackets[idx] += rxq->stats.ipackets;
325                         tmp.q_ibytes[idx] += rxq->stats.ibytes;
326 #endif
327                         tmp.q_errors[idx] += (rxq->stats.idropped +
328                                               rxq->stats.rx_nombuf);
329                 }
330 #ifdef MLX5_PMD_SOFT_COUNTERS
331                 tmp.ipackets += rxq->stats.ipackets;
332                 tmp.ibytes += rxq->stats.ibytes;
333 #endif
334                 tmp.ierrors += rxq->stats.idropped;
335                 tmp.rx_nombuf += rxq->stats.rx_nombuf;
336         }
337         for (i = 0; (i != priv->txqs_n); ++i) {
338                 struct txq *txq = (*priv->txqs)[i];
339
340                 if (txq == NULL)
341                         continue;
342                 idx = txq->stats.idx;
343                 if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
344 #ifdef MLX5_PMD_SOFT_COUNTERS
345                         tmp.q_opackets[idx] += txq->stats.opackets;
346                         tmp.q_obytes[idx] += txq->stats.obytes;
347 #endif
348                         tmp.q_errors[idx] += txq->stats.odropped;
349                 }
350 #ifdef MLX5_PMD_SOFT_COUNTERS
351                 tmp.opackets += txq->stats.opackets;
352                 tmp.obytes += txq->stats.obytes;
353 #endif
354                 tmp.oerrors += txq->stats.odropped;
355         }
356 #ifndef MLX5_PMD_SOFT_COUNTERS
357         /* FIXME: retrieve and add hardware counters. */
358 #endif
359         *stats = tmp;
360         priv_unlock(priv);
361 }
362
363 /**
364  * DPDK callback to clear device statistics.
365  *
366  * @param dev
367  *   Pointer to Ethernet device structure.
368  */
369 void
370 mlx5_stats_reset(struct rte_eth_dev *dev)
371 {
372         struct priv *priv = dev->data->dev_private;
373         unsigned int i;
374         unsigned int idx;
375
376         priv_lock(priv);
377         for (i = 0; (i != priv->rxqs_n); ++i) {
378                 if ((*priv->rxqs)[i] == NULL)
379                         continue;
380                 idx = (*priv->rxqs)[i]->stats.idx;
381                 (*priv->rxqs)[i]->stats =
382                         (struct mlx5_rxq_stats){ .idx = idx };
383         }
384         for (i = 0; (i != priv->txqs_n); ++i) {
385                 if ((*priv->txqs)[i] == NULL)
386                         continue;
387                 idx = (*priv->txqs)[i]->stats.idx;
388                 (*priv->txqs)[i]->stats =
389                         (struct mlx5_txq_stats){ .idx = idx };
390         }
391 #ifndef MLX5_PMD_SOFT_COUNTERS
392         /* FIXME: reset hardware counters. */
393 #endif
394         priv_unlock(priv);
395 }
396
397 /**
398  * DPDK callback to get extended device statistics.
399  *
400  * @param dev
401  *   Pointer to Ethernet device structure.
402  * @param[out] stats
403  *   Stats table output buffer.
404  * @param n
405  *   The size of the stats table.
406  *
407  * @return
408  *   Number of xstats on success, negative on failure.
409  */
410 int
411 mlx5_xstats_get(struct rte_eth_dev *dev,
412                 struct rte_eth_xstat *stats, unsigned int n)
413 {
414         struct priv *priv = mlx5_get_priv(dev);
415         int ret = xstats_n;
416
417         if (n >= xstats_n && stats) {
418                 priv_lock(priv);
419                 ret = priv_xstats_get(priv, stats);
420                 priv_unlock(priv);
421         }
422         return ret;
423 }
424
425 /**
426  * DPDK callback to clear device extended statistics.
427  *
428  * @param dev
429  *   Pointer to Ethernet device structure.
430  */
431 void
432 mlx5_xstats_reset(struct rte_eth_dev *dev)
433 {
434         struct priv *priv = mlx5_get_priv(dev);
435
436         priv_lock(priv);
437         priv_xstats_reset(priv);
438         priv_unlock(priv);
439 }
440
441 /**
442  * DPDK callback to retrieve names of extended device statistics
443  *
444  * @param dev
445  *   Pointer to Ethernet device structure.
446  * @param[out] xstats_names
447  *   Buffer to insert names into.
448  * @param n
449  *   Number of names.
450  *
451  * @return
452  *   Number of xstats names.
453  */
454 int
455 mlx5_xstats_get_names(struct rte_eth_dev *dev,
456                 struct rte_eth_xstat_name *xstats_names, unsigned int n)
457 {
458         struct priv *priv = mlx5_get_priv(dev);
459         unsigned int i;
460
461         if (n >= xstats_n && xstats_names) {
462                 priv_lock(priv);
463                 for (i = 0; i != xstats_n; ++i) {
464                         strncpy(xstats_names[i].name,
465                                 mlx5_counters_init[i].dpdk_name,
466                                 RTE_ETH_XSTATS_NAME_SIZE);
467                         xstats_names[i].name[RTE_ETH_XSTATS_NAME_SIZE - 1] = 0;
468                 }
469                 priv_unlock(priv);
470         }
471         return xstats_n;
472 }