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