]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/dpdk/drivers/net/mlx5/mlx5_stats.c
import 15.2.0 Octopus source
[ceph.git] / ceph / src / seastar / dpdk / drivers / net / mlx5 / mlx5_stats.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright 2015 6WIND S.A.
3 * Copyright 2015 Mellanox Technologies, Ltd
4 */
5
6 #include <inttypes.h>
7 #include <linux/sockios.h>
8 #include <linux/ethtool.h>
9 #include <stdint.h>
10 #include <stdio.h>
11
12 #include <rte_ethdev_driver.h>
13 #include <rte_common.h>
14 #include <rte_malloc.h>
15
16 #include "mlx5.h"
17 #include "mlx5_rxtx.h"
18 #include "mlx5_defs.h"
19
20 static const struct mlx5_counter_ctrl mlx5_counters_init[] = {
21 {
22 .dpdk_name = "rx_port_unicast_bytes",
23 .ctr_name = "rx_vport_unicast_bytes",
24 },
25 {
26 .dpdk_name = "rx_port_multicast_bytes",
27 .ctr_name = "rx_vport_multicast_bytes",
28 },
29 {
30 .dpdk_name = "rx_port_broadcast_bytes",
31 .ctr_name = "rx_vport_broadcast_bytes",
32 },
33 {
34 .dpdk_name = "rx_port_unicast_packets",
35 .ctr_name = "rx_vport_unicast_packets",
36 },
37 {
38 .dpdk_name = "rx_port_multicast_packets",
39 .ctr_name = "rx_vport_multicast_packets",
40 },
41 {
42 .dpdk_name = "rx_port_broadcast_packets",
43 .ctr_name = "rx_vport_broadcast_packets",
44 },
45 {
46 .dpdk_name = "tx_port_unicast_bytes",
47 .ctr_name = "tx_vport_unicast_bytes",
48 },
49 {
50 .dpdk_name = "tx_port_multicast_bytes",
51 .ctr_name = "tx_vport_multicast_bytes",
52 },
53 {
54 .dpdk_name = "tx_port_broadcast_bytes",
55 .ctr_name = "tx_vport_broadcast_bytes",
56 },
57 {
58 .dpdk_name = "tx_port_unicast_packets",
59 .ctr_name = "tx_vport_unicast_packets",
60 },
61 {
62 .dpdk_name = "tx_port_multicast_packets",
63 .ctr_name = "tx_vport_multicast_packets",
64 },
65 {
66 .dpdk_name = "tx_port_broadcast_packets",
67 .ctr_name = "tx_vport_broadcast_packets",
68 },
69 {
70 .dpdk_name = "rx_wqe_err",
71 .ctr_name = "rx_wqe_err",
72 },
73 {
74 .dpdk_name = "rx_crc_errors_phy",
75 .ctr_name = "rx_crc_errors_phy",
76 },
77 {
78 .dpdk_name = "rx_in_range_len_errors_phy",
79 .ctr_name = "rx_in_range_len_errors_phy",
80 },
81 {
82 .dpdk_name = "rx_symbol_err_phy",
83 .ctr_name = "rx_symbol_err_phy",
84 },
85 {
86 .dpdk_name = "tx_errors_phy",
87 .ctr_name = "tx_errors_phy",
88 },
89 {
90 .dpdk_name = "rx_out_of_buffer",
91 .ctr_name = "out_of_buffer",
92 .ib = 1,
93 },
94 {
95 .dpdk_name = "tx_packets_phy",
96 .ctr_name = "tx_packets_phy",
97 },
98 {
99 .dpdk_name = "rx_packets_phy",
100 .ctr_name = "rx_packets_phy",
101 },
102 {
103 .dpdk_name = "tx_discards_phy",
104 .ctr_name = "tx_discards_phy",
105 },
106 {
107 .dpdk_name = "rx_discards_phy",
108 .ctr_name = "rx_discards_phy",
109 },
110 {
111 .dpdk_name = "tx_bytes_phy",
112 .ctr_name = "tx_bytes_phy",
113 },
114 {
115 .dpdk_name = "rx_bytes_phy",
116 .ctr_name = "rx_bytes_phy",
117 },
118 /* Representor only */
119 {
120 .dpdk_name = "rx_packets",
121 .ctr_name = "vport_rx_packets",
122 },
123 {
124 .dpdk_name = "rx_bytes",
125 .ctr_name = "vport_rx_bytes",
126 },
127 {
128 .dpdk_name = "tx_packets",
129 .ctr_name = "vport_tx_packets",
130 },
131 {
132 .dpdk_name = "tx_bytes",
133 .ctr_name = "vport_tx_bytes",
134 },
135 };
136
137 static const unsigned int xstats_n = RTE_DIM(mlx5_counters_init);
138
139 static inline void
140 mlx5_read_ib_stat(struct mlx5_priv *priv, const char *ctr_name, uint64_t *stat)
141 {
142 FILE *file;
143 if (priv->sh) {
144 MKSTR(path, "%s/ports/%d/hw_counters/%s",
145 priv->sh->ibdev_path,
146 priv->ibv_port,
147 ctr_name);
148
149 file = fopen(path, "rb");
150 if (file) {
151 int n = fscanf(file, "%" SCNu64, stat);
152
153 fclose(file);
154 if (n == 1)
155 return;
156 }
157 }
158 *stat = 0;
159 }
160
161 /**
162 * Read device counters table.
163 *
164 * @param dev
165 * Pointer to Ethernet device.
166 * @param[out] stats
167 * Counters table output buffer.
168 *
169 * @return
170 * 0 on success and stats is filled, negative errno value otherwise and
171 * rte_errno is set.
172 */
173 static int
174 mlx5_read_dev_counters(struct rte_eth_dev *dev, uint64_t *stats)
175 {
176 struct mlx5_priv *priv = dev->data->dev_private;
177 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
178 unsigned int i;
179 struct ifreq ifr;
180 unsigned int stats_sz = xstats_ctrl->stats_n * sizeof(uint64_t);
181 unsigned char et_stat_buf[sizeof(struct ethtool_stats) + stats_sz];
182 struct ethtool_stats *et_stats = (struct ethtool_stats *)et_stat_buf;
183 int ret;
184
185 et_stats->cmd = ETHTOOL_GSTATS;
186 et_stats->n_stats = xstats_ctrl->stats_n;
187 ifr.ifr_data = (caddr_t)et_stats;
188 ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
189 if (ret) {
190 DRV_LOG(WARNING,
191 "port %u unable to read statistic values from device",
192 dev->data->port_id);
193 return ret;
194 }
195 for (i = 0; i != xstats_ctrl->mlx5_stats_n; ++i) {
196 if (xstats_ctrl->info[i].ib) {
197 mlx5_read_ib_stat(priv, xstats_ctrl->info[i].ctr_name,
198 &stats[i]);
199 } else {
200 stats[i] = (uint64_t)
201 et_stats->data[xstats_ctrl->dev_table_idx[i]];
202 }
203 }
204 return 0;
205 }
206
207 /**
208 * Query the number of statistics provided by ETHTOOL.
209 *
210 * @param dev
211 * Pointer to Ethernet device.
212 *
213 * @return
214 * Number of statistics on success, negative errno value otherwise and
215 * rte_errno is set.
216 */
217 static int
218 mlx5_ethtool_get_stats_n(struct rte_eth_dev *dev) {
219 struct ethtool_drvinfo drvinfo;
220 struct ifreq ifr;
221 int ret;
222
223 drvinfo.cmd = ETHTOOL_GDRVINFO;
224 ifr.ifr_data = (caddr_t)&drvinfo;
225 ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
226 if (ret) {
227 DRV_LOG(WARNING, "port %u unable to query number of statistics",
228 dev->data->port_id);
229 return ret;
230 }
231 return drvinfo.n_stats;
232 }
233
234 /**
235 * Init the structures to read device counters.
236 *
237 * @param dev
238 * Pointer to Ethernet device.
239 */
240 void
241 mlx5_stats_init(struct rte_eth_dev *dev)
242 {
243 struct mlx5_priv *priv = dev->data->dev_private;
244 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
245 struct mlx5_stats_ctrl *stats_ctrl = &priv->stats_ctrl;
246 unsigned int i;
247 unsigned int j;
248 struct ifreq ifr;
249 struct ethtool_gstrings *strings = NULL;
250 unsigned int dev_stats_n;
251 unsigned int str_sz;
252 int ret;
253
254 /* So that it won't aggregate for each init. */
255 xstats_ctrl->mlx5_stats_n = 0;
256 ret = mlx5_ethtool_get_stats_n(dev);
257 if (ret < 0) {
258 DRV_LOG(WARNING, "port %u no extended statistics available",
259 dev->data->port_id);
260 return;
261 }
262 dev_stats_n = ret;
263 /* Allocate memory to grab stat names and values. */
264 str_sz = dev_stats_n * ETH_GSTRING_LEN;
265 strings = (struct ethtool_gstrings *)
266 rte_malloc("xstats_strings",
267 str_sz + sizeof(struct ethtool_gstrings), 0);
268 if (!strings) {
269 DRV_LOG(WARNING, "port %u unable to allocate memory for xstats",
270 dev->data->port_id);
271 return;
272 }
273 strings->cmd = ETHTOOL_GSTRINGS;
274 strings->string_set = ETH_SS_STATS;
275 strings->len = dev_stats_n;
276 ifr.ifr_data = (caddr_t)strings;
277 ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
278 if (ret) {
279 DRV_LOG(WARNING, "port %u unable to get statistic names",
280 dev->data->port_id);
281 goto free;
282 }
283 for (i = 0; i != dev_stats_n; ++i) {
284 const char *curr_string = (const char *)
285 &strings->data[i * ETH_GSTRING_LEN];
286
287 for (j = 0; j != xstats_n; ++j) {
288 if (!strcmp(mlx5_counters_init[j].ctr_name,
289 curr_string)) {
290 unsigned int idx = xstats_ctrl->mlx5_stats_n++;
291
292 xstats_ctrl->dev_table_idx[idx] = i;
293 xstats_ctrl->info[idx] = mlx5_counters_init[j];
294 break;
295 }
296 }
297 }
298 /* Add IB counters. */
299 for (i = 0; i != xstats_n; ++i) {
300 if (mlx5_counters_init[i].ib) {
301 unsigned int idx = xstats_ctrl->mlx5_stats_n++;
302
303 xstats_ctrl->info[idx] = mlx5_counters_init[i];
304 }
305 }
306 assert(xstats_ctrl->mlx5_stats_n <= MLX5_MAX_XSTATS);
307 xstats_ctrl->stats_n = dev_stats_n;
308 /* Copy to base at first time. */
309 ret = mlx5_read_dev_counters(dev, xstats_ctrl->base);
310 if (ret)
311 DRV_LOG(ERR, "port %u cannot read device counters: %s",
312 dev->data->port_id, strerror(rte_errno));
313 mlx5_read_ib_stat(priv, "out_of_buffer", &stats_ctrl->imissed_base);
314 free:
315 rte_free(strings);
316 }
317
318 /**
319 * DPDK callback to get extended device statistics.
320 *
321 * @param dev
322 * Pointer to Ethernet device.
323 * @param[out] stats
324 * Pointer to rte extended stats table.
325 * @param n
326 * The size of the stats table.
327 *
328 * @return
329 * Number of extended stats on success and stats is filled,
330 * negative on error and rte_errno is set.
331 */
332 int
333 mlx5_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstat *stats,
334 unsigned int n)
335 {
336 struct mlx5_priv *priv = dev->data->dev_private;
337 unsigned int i;
338 uint64_t counters[n];
339 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
340 uint16_t mlx5_stats_n = xstats_ctrl->mlx5_stats_n;
341
342 if (n >= mlx5_stats_n && stats) {
343 int stats_n;
344 int ret;
345
346 stats_n = mlx5_ethtool_get_stats_n(dev);
347 if (stats_n < 0)
348 return stats_n;
349 if (xstats_ctrl->stats_n != stats_n)
350 mlx5_stats_init(dev);
351 ret = mlx5_read_dev_counters(dev, counters);
352 if (ret)
353 return ret;
354 for (i = 0; i != mlx5_stats_n; ++i) {
355 stats[i].id = i;
356 stats[i].value = (counters[i] - xstats_ctrl->base[i]);
357 }
358 }
359 return mlx5_stats_n;
360 }
361
362 /**
363 * DPDK callback to get device statistics.
364 *
365 * @param dev
366 * Pointer to Ethernet device structure.
367 * @param[out] stats
368 * Stats structure output buffer.
369 *
370 * @return
371 * 0 on success and stats is filled, negative errno value otherwise and
372 * rte_errno is set.
373 */
374 int
375 mlx5_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
376 {
377 struct mlx5_priv *priv = dev->data->dev_private;
378 struct rte_eth_stats tmp;
379 unsigned int i;
380 unsigned int idx;
381
382 memset(&tmp, 0, sizeof(tmp));
383 /* Add software counters. */
384 for (i = 0; (i != priv->rxqs_n); ++i) {
385 struct mlx5_rxq_data *rxq = (*priv->rxqs)[i];
386
387 if (rxq == NULL)
388 continue;
389 idx = rxq->idx;
390 if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
391 #ifdef MLX5_PMD_SOFT_COUNTERS
392 tmp.q_ipackets[idx] += rxq->stats.ipackets;
393 tmp.q_ibytes[idx] += rxq->stats.ibytes;
394 #endif
395 tmp.q_errors[idx] += (rxq->stats.idropped +
396 rxq->stats.rx_nombuf);
397 }
398 #ifdef MLX5_PMD_SOFT_COUNTERS
399 tmp.ipackets += rxq->stats.ipackets;
400 tmp.ibytes += rxq->stats.ibytes;
401 #endif
402 tmp.ierrors += rxq->stats.idropped;
403 tmp.rx_nombuf += rxq->stats.rx_nombuf;
404 }
405 for (i = 0; (i != priv->txqs_n); ++i) {
406 struct mlx5_txq_data *txq = (*priv->txqs)[i];
407
408 if (txq == NULL)
409 continue;
410 idx = txq->idx;
411 if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
412 #ifdef MLX5_PMD_SOFT_COUNTERS
413 tmp.q_opackets[idx] += txq->stats.opackets;
414 tmp.q_obytes[idx] += txq->stats.obytes;
415 #endif
416 tmp.q_errors[idx] += txq->stats.oerrors;
417 }
418 #ifdef MLX5_PMD_SOFT_COUNTERS
419 tmp.opackets += txq->stats.opackets;
420 tmp.obytes += txq->stats.obytes;
421 #endif
422 tmp.oerrors += txq->stats.oerrors;
423 }
424 mlx5_read_ib_stat(priv, "out_of_buffer", &tmp.imissed);
425 tmp.imissed -= priv->stats_ctrl.imissed_base;
426 #ifndef MLX5_PMD_SOFT_COUNTERS
427 /* FIXME: retrieve and add hardware counters. */
428 #endif
429 *stats = tmp;
430 return 0;
431 }
432
433 /**
434 * DPDK callback to clear device statistics.
435 *
436 * @param dev
437 * Pointer to Ethernet device structure.
438 */
439 void
440 mlx5_stats_reset(struct rte_eth_dev *dev)
441 {
442 struct mlx5_priv *priv = dev->data->dev_private;
443 struct mlx5_stats_ctrl *stats_ctrl = &priv->stats_ctrl;
444 unsigned int i;
445
446 for (i = 0; (i != priv->rxqs_n); ++i) {
447 if ((*priv->rxqs)[i] == NULL)
448 continue;
449 memset(&(*priv->rxqs)[i]->stats, 0,
450 sizeof(struct mlx5_rxq_stats));
451 }
452 for (i = 0; (i != priv->txqs_n); ++i) {
453 if ((*priv->txqs)[i] == NULL)
454 continue;
455 memset(&(*priv->txqs)[i]->stats, 0,
456 sizeof(struct mlx5_txq_stats));
457 }
458 mlx5_read_ib_stat(priv, "out_of_buffer", &stats_ctrl->imissed_base);
459 #ifndef MLX5_PMD_SOFT_COUNTERS
460 /* FIXME: reset hardware counters. */
461 #endif
462 }
463
464 /**
465 * DPDK callback to clear device extended statistics.
466 *
467 * @param dev
468 * Pointer to Ethernet device structure.
469 */
470 void
471 mlx5_xstats_reset(struct rte_eth_dev *dev)
472 {
473 struct mlx5_priv *priv = dev->data->dev_private;
474 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
475 int stats_n;
476 unsigned int i;
477 unsigned int n = xstats_ctrl->mlx5_stats_n;
478 uint64_t counters[n];
479 int ret;
480
481 stats_n = mlx5_ethtool_get_stats_n(dev);
482 if (stats_n < 0) {
483 DRV_LOG(ERR, "port %u cannot get stats: %s", dev->data->port_id,
484 strerror(-stats_n));
485 return;
486 }
487 if (xstats_ctrl->stats_n != stats_n)
488 mlx5_stats_init(dev);
489 ret = mlx5_read_dev_counters(dev, counters);
490 if (ret) {
491 DRV_LOG(ERR, "port %u cannot read device counters: %s",
492 dev->data->port_id, strerror(rte_errno));
493 return;
494 }
495 for (i = 0; i != n; ++i)
496 xstats_ctrl->base[i] = counters[i];
497 }
498
499 /**
500 * DPDK callback to retrieve names of extended device statistics
501 *
502 * @param dev
503 * Pointer to Ethernet device structure.
504 * @param[out] xstats_names
505 * Buffer to insert names into.
506 * @param n
507 * Number of names.
508 *
509 * @return
510 * Number of xstats names.
511 */
512 int
513 mlx5_xstats_get_names(struct rte_eth_dev *dev __rte_unused,
514 struct rte_eth_xstat_name *xstats_names, unsigned int n)
515 {
516 unsigned int i;
517 struct mlx5_priv *priv = dev->data->dev_private;
518 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
519 unsigned int mlx5_xstats_n = xstats_ctrl->mlx5_stats_n;
520
521 if (n >= mlx5_xstats_n && xstats_names) {
522 for (i = 0; i != mlx5_xstats_n; ++i) {
523 strncpy(xstats_names[i].name,
524 xstats_ctrl->info[i].dpdk_name,
525 RTE_ETH_XSTATS_NAME_SIZE);
526 xstats_names[i].name[RTE_ETH_XSTATS_NAME_SIZE - 1] = 0;
527 }
528 }
529 return mlx5_xstats_n;
530 }