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->stats.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->stats.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 unsigned int idx; 446 447 for (i = 0; (i != priv->rxqs_n); ++i) { 448 if ((*priv->rxqs)[i] == NULL) 449 continue; 450 idx = (*priv->rxqs)[i]->stats.idx; 451 (*priv->rxqs)[i]->stats = 452 (struct mlx5_rxq_stats){ .idx = idx }; 453 } 454 for (i = 0; (i != priv->txqs_n); ++i) { 455 if ((*priv->txqs)[i] == NULL) 456 continue; 457 idx = (*priv->txqs)[i]->stats.idx; 458 (*priv->txqs)[i]->stats = 459 (struct mlx5_txq_stats){ .idx = idx }; 460 } 461 mlx5_read_ib_stat(priv, "out_of_buffer", &stats_ctrl->imissed_base); 462 #ifndef MLX5_PMD_SOFT_COUNTERS 463 /* FIXME: reset hardware counters. */ 464 #endif 465 } 466 467 /** 468 * DPDK callback to clear device extended statistics. 469 * 470 * @param dev 471 * Pointer to Ethernet device structure. 472 */ 473 void 474 mlx5_xstats_reset(struct rte_eth_dev *dev) 475 { 476 struct mlx5_priv *priv = dev->data->dev_private; 477 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; 478 int stats_n; 479 unsigned int i; 480 unsigned int n = xstats_ctrl->mlx5_stats_n; 481 uint64_t counters[n]; 482 int ret; 483 484 stats_n = mlx5_ethtool_get_stats_n(dev); 485 if (stats_n < 0) { 486 DRV_LOG(ERR, "port %u cannot get stats: %s", dev->data->port_id, 487 strerror(-stats_n)); 488 return; 489 } 490 if (xstats_ctrl->stats_n != stats_n) 491 mlx5_stats_init(dev); 492 ret = mlx5_read_dev_counters(dev, counters); 493 if (ret) { 494 DRV_LOG(ERR, "port %u cannot read device counters: %s", 495 dev->data->port_id, strerror(rte_errno)); 496 return; 497 } 498 for (i = 0; i != n; ++i) 499 xstats_ctrl->base[i] = counters[i]; 500 } 501 502 /** 503 * DPDK callback to retrieve names of extended device statistics 504 * 505 * @param dev 506 * Pointer to Ethernet device structure. 507 * @param[out] xstats_names 508 * Buffer to insert names into. 509 * @param n 510 * Number of names. 511 * 512 * @return 513 * Number of xstats names. 514 */ 515 int 516 mlx5_xstats_get_names(struct rte_eth_dev *dev __rte_unused, 517 struct rte_eth_xstat_name *xstats_names, unsigned int n) 518 { 519 unsigned int i; 520 struct mlx5_priv *priv = dev->data->dev_private; 521 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; 522 unsigned int mlx5_xstats_n = xstats_ctrl->mlx5_stats_n; 523 524 if (n >= mlx5_xstats_n && xstats_names) { 525 for (i = 0; i != mlx5_xstats_n; ++i) { 526 strncpy(xstats_names[i].name, 527 xstats_ctrl->info[i].dpdk_name, 528 RTE_ETH_XSTATS_NAME_SIZE); 529 xstats_names[i].name[RTE_ETH_XSTATS_NAME_SIZE - 1] = 0; 530 } 531 } 532 return mlx5_xstats_n; 533 } 534