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 priv *priv, const char *ctr_name, uint64_t *stat) 141 { 142 FILE *file; 143 MKSTR(path, "%s/ports/1/hw_counters/%s", 144 priv->ibdev_path, 145 ctr_name); 146 147 file = fopen(path, "rb"); 148 if (file) { 149 int n = fscanf(file, "%" SCNu64, stat); 150 151 fclose(file); 152 if (n != 1) 153 stat = 0; 154 } 155 } 156 157 /** 158 * Read device counters table. 159 * 160 * @param dev 161 * Pointer to Ethernet device. 162 * @param[out] stats 163 * Counters table output buffer. 164 * 165 * @return 166 * 0 on success and stats is filled, negative errno value otherwise and 167 * rte_errno is set. 168 */ 169 static int 170 mlx5_read_dev_counters(struct rte_eth_dev *dev, uint64_t *stats) 171 { 172 struct priv *priv = dev->data->dev_private; 173 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; 174 unsigned int i; 175 struct ifreq ifr; 176 unsigned int stats_sz = xstats_ctrl->stats_n * sizeof(uint64_t); 177 unsigned char et_stat_buf[sizeof(struct ethtool_stats) + stats_sz]; 178 struct ethtool_stats *et_stats = (struct ethtool_stats *)et_stat_buf; 179 int ret; 180 181 et_stats->cmd = ETHTOOL_GSTATS; 182 et_stats->n_stats = xstats_ctrl->stats_n; 183 ifr.ifr_data = (caddr_t)et_stats; 184 ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr); 185 if (ret) { 186 DRV_LOG(WARNING, 187 "port %u unable to read statistic values from device", 188 dev->data->port_id); 189 return ret; 190 } 191 for (i = 0; i != xstats_ctrl->mlx5_stats_n; ++i) { 192 if (xstats_ctrl->info[i].ib) { 193 mlx5_read_ib_stat(priv, xstats_ctrl->info[i].ctr_name, 194 &stats[i]); 195 } else { 196 stats[i] = (uint64_t) 197 et_stats->data[xstats_ctrl->dev_table_idx[i]]; 198 } 199 } 200 return 0; 201 } 202 203 /** 204 * Query the number of statistics provided by ETHTOOL. 205 * 206 * @param dev 207 * Pointer to Ethernet device. 208 * 209 * @return 210 * Number of statistics on success, negative errno value otherwise and 211 * rte_errno is set. 212 */ 213 static int 214 mlx5_ethtool_get_stats_n(struct rte_eth_dev *dev) { 215 struct ethtool_drvinfo drvinfo; 216 struct ifreq ifr; 217 int ret; 218 219 drvinfo.cmd = ETHTOOL_GDRVINFO; 220 ifr.ifr_data = (caddr_t)&drvinfo; 221 ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr); 222 if (ret) { 223 DRV_LOG(WARNING, "port %u unable to query number of statistics", 224 dev->data->port_id); 225 return ret; 226 } 227 return drvinfo.n_stats; 228 } 229 230 /** 231 * Init the structures to read device counters. 232 * 233 * @param dev 234 * Pointer to Ethernet device. 235 */ 236 void 237 mlx5_stats_init(struct rte_eth_dev *dev) 238 { 239 struct priv *priv = dev->data->dev_private; 240 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; 241 struct mlx5_stats_ctrl *stats_ctrl = &priv->stats_ctrl; 242 unsigned int i; 243 unsigned int j; 244 struct ifreq ifr; 245 struct ethtool_gstrings *strings = NULL; 246 unsigned int dev_stats_n; 247 unsigned int str_sz; 248 int ret; 249 250 /* So that it won't aggregate for each init. */ 251 xstats_ctrl->mlx5_stats_n = 0; 252 ret = mlx5_ethtool_get_stats_n(dev); 253 if (ret < 0) { 254 DRV_LOG(WARNING, "port %u no extended statistics available", 255 dev->data->port_id); 256 return; 257 } 258 dev_stats_n = ret; 259 /* Allocate memory to grab stat names and values. */ 260 str_sz = dev_stats_n * ETH_GSTRING_LEN; 261 strings = (struct ethtool_gstrings *) 262 rte_malloc("xstats_strings", 263 str_sz + sizeof(struct ethtool_gstrings), 0); 264 if (!strings) { 265 DRV_LOG(WARNING, "port %u unable to allocate memory for xstats", 266 dev->data->port_id); 267 return; 268 } 269 strings->cmd = ETHTOOL_GSTRINGS; 270 strings->string_set = ETH_SS_STATS; 271 strings->len = dev_stats_n; 272 ifr.ifr_data = (caddr_t)strings; 273 ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr); 274 if (ret) { 275 DRV_LOG(WARNING, "port %u unable to get statistic names", 276 dev->data->port_id); 277 goto free; 278 } 279 for (i = 0; i != dev_stats_n; ++i) { 280 const char *curr_string = (const char *) 281 &strings->data[i * ETH_GSTRING_LEN]; 282 283 for (j = 0; j != xstats_n; ++j) { 284 if (!strcmp(mlx5_counters_init[j].ctr_name, 285 curr_string)) { 286 unsigned int idx = xstats_ctrl->mlx5_stats_n++; 287 288 xstats_ctrl->dev_table_idx[idx] = i; 289 xstats_ctrl->info[idx] = mlx5_counters_init[j]; 290 break; 291 } 292 } 293 } 294 /* Add IB counters. */ 295 for (i = 0; i != xstats_n; ++i) { 296 if (mlx5_counters_init[i].ib) { 297 unsigned int idx = xstats_ctrl->mlx5_stats_n++; 298 299 xstats_ctrl->info[idx] = mlx5_counters_init[i]; 300 } 301 } 302 assert(xstats_ctrl->mlx5_stats_n <= MLX5_MAX_XSTATS); 303 xstats_ctrl->stats_n = dev_stats_n; 304 /* Copy to base at first time. */ 305 ret = mlx5_read_dev_counters(dev, xstats_ctrl->base); 306 if (ret) 307 DRV_LOG(ERR, "port %u cannot read device counters: %s", 308 dev->data->port_id, strerror(rte_errno)); 309 mlx5_read_ib_stat(priv, "out_of_buffer", &stats_ctrl->imissed_base); 310 free: 311 rte_free(strings); 312 } 313 314 /** 315 * DPDK callback to get extended device statistics. 316 * 317 * @param dev 318 * Pointer to Ethernet device. 319 * @param[out] stats 320 * Pointer to rte extended stats table. 321 * @param n 322 * The size of the stats table. 323 * 324 * @return 325 * Number of extended stats on success and stats is filled, 326 * negative on error and rte_errno is set. 327 */ 328 int 329 mlx5_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstat *stats, 330 unsigned int n) 331 { 332 struct priv *priv = dev->data->dev_private; 333 unsigned int i; 334 uint64_t counters[n]; 335 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; 336 uint16_t mlx5_stats_n = xstats_ctrl->mlx5_stats_n; 337 338 if (n >= mlx5_stats_n && stats) { 339 int stats_n; 340 int ret; 341 342 stats_n = mlx5_ethtool_get_stats_n(dev); 343 if (stats_n < 0) 344 return stats_n; 345 if (xstats_ctrl->stats_n != stats_n) 346 mlx5_stats_init(dev); 347 ret = mlx5_read_dev_counters(dev, counters); 348 if (ret) 349 return ret; 350 for (i = 0; i != mlx5_stats_n; ++i) { 351 stats[i].id = i; 352 stats[i].value = (counters[i] - xstats_ctrl->base[i]); 353 } 354 } 355 return mlx5_stats_n; 356 } 357 358 /** 359 * DPDK callback to get device statistics. 360 * 361 * @param dev 362 * Pointer to Ethernet device structure. 363 * @param[out] stats 364 * Stats structure output buffer. 365 * 366 * @return 367 * 0 on success and stats is filled, negative errno value otherwise and 368 * rte_errno is set. 369 */ 370 int 371 mlx5_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) 372 { 373 struct priv *priv = dev->data->dev_private; 374 struct rte_eth_stats tmp; 375 unsigned int i; 376 unsigned int idx; 377 378 memset(&tmp, 0, sizeof(tmp)); 379 /* Add software counters. */ 380 for (i = 0; (i != priv->rxqs_n); ++i) { 381 struct mlx5_rxq_data *rxq = (*priv->rxqs)[i]; 382 383 if (rxq == NULL) 384 continue; 385 idx = rxq->stats.idx; 386 if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) { 387 #ifdef MLX5_PMD_SOFT_COUNTERS 388 tmp.q_ipackets[idx] += rxq->stats.ipackets; 389 tmp.q_ibytes[idx] += rxq->stats.ibytes; 390 #endif 391 tmp.q_errors[idx] += (rxq->stats.idropped + 392 rxq->stats.rx_nombuf); 393 } 394 #ifdef MLX5_PMD_SOFT_COUNTERS 395 tmp.ipackets += rxq->stats.ipackets; 396 tmp.ibytes += rxq->stats.ibytes; 397 #endif 398 tmp.ierrors += rxq->stats.idropped; 399 tmp.rx_nombuf += rxq->stats.rx_nombuf; 400 } 401 for (i = 0; (i != priv->txqs_n); ++i) { 402 struct mlx5_txq_data *txq = (*priv->txqs)[i]; 403 404 if (txq == NULL) 405 continue; 406 idx = txq->stats.idx; 407 if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) { 408 #ifdef MLX5_PMD_SOFT_COUNTERS 409 tmp.q_opackets[idx] += txq->stats.opackets; 410 tmp.q_obytes[idx] += txq->stats.obytes; 411 #endif 412 tmp.q_errors[idx] += txq->stats.oerrors; 413 } 414 #ifdef MLX5_PMD_SOFT_COUNTERS 415 tmp.opackets += txq->stats.opackets; 416 tmp.obytes += txq->stats.obytes; 417 #endif 418 tmp.oerrors += txq->stats.oerrors; 419 } 420 mlx5_read_ib_stat(priv, "out_of_buffer", &tmp.imissed); 421 tmp.imissed -= priv->stats_ctrl.imissed_base; 422 #ifndef MLX5_PMD_SOFT_COUNTERS 423 /* FIXME: retrieve and add hardware counters. */ 424 #endif 425 *stats = tmp; 426 return 0; 427 } 428 429 /** 430 * DPDK callback to clear device statistics. 431 * 432 * @param dev 433 * Pointer to Ethernet device structure. 434 */ 435 void 436 mlx5_stats_reset(struct rte_eth_dev *dev) 437 { 438 struct priv *priv = dev->data->dev_private; 439 struct mlx5_stats_ctrl *stats_ctrl = &priv->stats_ctrl; 440 unsigned int i; 441 unsigned int idx; 442 443 for (i = 0; (i != priv->rxqs_n); ++i) { 444 if ((*priv->rxqs)[i] == NULL) 445 continue; 446 idx = (*priv->rxqs)[i]->stats.idx; 447 (*priv->rxqs)[i]->stats = 448 (struct mlx5_rxq_stats){ .idx = idx }; 449 } 450 for (i = 0; (i != priv->txqs_n); ++i) { 451 if ((*priv->txqs)[i] == NULL) 452 continue; 453 idx = (*priv->txqs)[i]->stats.idx; 454 (*priv->txqs)[i]->stats = 455 (struct mlx5_txq_stats){ .idx = idx }; 456 } 457 mlx5_read_ib_stat(priv, "out_of_buffer", &stats_ctrl->imissed_base); 458 #ifndef MLX5_PMD_SOFT_COUNTERS 459 /* FIXME: reset hardware counters. */ 460 #endif 461 } 462 463 /** 464 * DPDK callback to clear device extended statistics. 465 * 466 * @param dev 467 * Pointer to Ethernet device structure. 468 */ 469 void 470 mlx5_xstats_reset(struct rte_eth_dev *dev) 471 { 472 struct priv *priv = dev->data->dev_private; 473 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; 474 int stats_n; 475 unsigned int i; 476 unsigned int n = xstats_ctrl->mlx5_stats_n; 477 uint64_t counters[n]; 478 int ret; 479 480 stats_n = mlx5_ethtool_get_stats_n(dev); 481 if (stats_n < 0) { 482 DRV_LOG(ERR, "port %u cannot get stats: %s", dev->data->port_id, 483 strerror(-stats_n)); 484 return; 485 } 486 if (xstats_ctrl->stats_n != stats_n) 487 mlx5_stats_init(dev); 488 ret = mlx5_read_dev_counters(dev, counters); 489 if (ret) { 490 DRV_LOG(ERR, "port %u cannot read device counters: %s", 491 dev->data->port_id, strerror(rte_errno)); 492 return; 493 } 494 for (i = 0; i != n; ++i) 495 xstats_ctrl->base[i] = counters[i]; 496 } 497 498 /** 499 * DPDK callback to retrieve names of extended device statistics 500 * 501 * @param dev 502 * Pointer to Ethernet device structure. 503 * @param[out] xstats_names 504 * Buffer to insert names into. 505 * @param n 506 * Number of names. 507 * 508 * @return 509 * Number of xstats names. 510 */ 511 int 512 mlx5_xstats_get_names(struct rte_eth_dev *dev __rte_unused, 513 struct rte_eth_xstat_name *xstats_names, unsigned int n) 514 { 515 unsigned int i; 516 struct priv *priv = dev->data->dev_private; 517 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; 518 unsigned int mlx5_xstats_n = xstats_ctrl->mlx5_stats_n; 519 520 if (n >= mlx5_xstats_n && xstats_names) { 521 for (i = 0; i != mlx5_xstats_n; ++i) { 522 strncpy(xstats_names[i].name, 523 xstats_ctrl->info[i].dpdk_name, 524 RTE_ETH_XSTATS_NAME_SIZE); 525 xstats_names[i].name[RTE_ETH_XSTATS_NAME_SIZE - 1] = 0; 526 } 527 } 528 return mlx5_xstats_n; 529 } 530