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