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