1 /*- 2 * BSD LICENSE 3 * 4 * Copyright 2015 6WIND S.A. 5 * Copyright 2015 Mellanox. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of 6WIND S.A. nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <linux/sockios.h> 35 #include <linux/ethtool.h> 36 37 #include <rte_ethdev_driver.h> 38 #include <rte_common.h> 39 #include <rte_malloc.h> 40 41 #include "mlx5.h" 42 #include "mlx5_rxtx.h" 43 #include "mlx5_defs.h" 44 45 struct mlx5_counter_ctrl { 46 /* Name of the counter. */ 47 char dpdk_name[RTE_ETH_XSTATS_NAME_SIZE]; 48 /* Name of the counter on the device table. */ 49 char ctr_name[RTE_ETH_XSTATS_NAME_SIZE]; 50 }; 51 52 static const struct mlx5_counter_ctrl mlx5_counters_init[] = { 53 { 54 .dpdk_name = "rx_port_unicast_bytes", 55 .ctr_name = "rx_vport_unicast_bytes", 56 }, 57 { 58 .dpdk_name = "rx_port_multicast_bytes", 59 .ctr_name = "rx_vport_multicast_bytes", 60 }, 61 { 62 .dpdk_name = "rx_port_broadcast_bytes", 63 .ctr_name = "rx_vport_broadcast_bytes", 64 }, 65 { 66 .dpdk_name = "rx_port_unicast_packets", 67 .ctr_name = "rx_vport_unicast_packets", 68 }, 69 { 70 .dpdk_name = "rx_port_multicast_packets", 71 .ctr_name = "rx_vport_multicast_packets", 72 }, 73 { 74 .dpdk_name = "rx_port_broadcast_packets", 75 .ctr_name = "rx_vport_broadcast_packets", 76 }, 77 { 78 .dpdk_name = "tx_port_unicast_bytes", 79 .ctr_name = "tx_vport_unicast_bytes", 80 }, 81 { 82 .dpdk_name = "tx_port_multicast_bytes", 83 .ctr_name = "tx_vport_multicast_bytes", 84 }, 85 { 86 .dpdk_name = "tx_port_broadcast_bytes", 87 .ctr_name = "tx_vport_broadcast_bytes", 88 }, 89 { 90 .dpdk_name = "tx_port_unicast_packets", 91 .ctr_name = "tx_vport_unicast_packets", 92 }, 93 { 94 .dpdk_name = "tx_port_multicast_packets", 95 .ctr_name = "tx_vport_multicast_packets", 96 }, 97 { 98 .dpdk_name = "tx_port_broadcast_packets", 99 .ctr_name = "tx_vport_broadcast_packets", 100 }, 101 { 102 .dpdk_name = "rx_wqe_err", 103 .ctr_name = "rx_wqe_err", 104 }, 105 { 106 .dpdk_name = "rx_crc_errors_phy", 107 .ctr_name = "rx_crc_errors_phy", 108 }, 109 { 110 .dpdk_name = "rx_in_range_len_errors_phy", 111 .ctr_name = "rx_in_range_len_errors_phy", 112 }, 113 { 114 .dpdk_name = "rx_symbol_err_phy", 115 .ctr_name = "rx_symbol_err_phy", 116 }, 117 { 118 .dpdk_name = "tx_errors_phy", 119 .ctr_name = "tx_errors_phy", 120 }, 121 { 122 .dpdk_name = "rx_out_of_buffer", 123 .ctr_name = "out_of_buffer", 124 }, 125 { 126 .dpdk_name = "tx_packets_phy", 127 .ctr_name = "tx_packets_phy", 128 }, 129 { 130 .dpdk_name = "rx_packets_phy", 131 .ctr_name = "rx_packets_phy", 132 }, 133 { 134 .dpdk_name = "tx_bytes_phy", 135 .ctr_name = "tx_bytes_phy", 136 }, 137 { 138 .dpdk_name = "rx_bytes_phy", 139 .ctr_name = "rx_bytes_phy", 140 }, 141 }; 142 143 static const unsigned int xstats_n = RTE_DIM(mlx5_counters_init); 144 145 /** 146 * Read device counters table. 147 * 148 * @param priv 149 * Pointer to private structure. 150 * @param[out] stats 151 * Counters table output buffer. 152 * 153 * @return 154 * 0 on success and stats is filled, negative on error. 155 */ 156 static int 157 priv_read_dev_counters(struct priv *priv, uint64_t *stats) 158 { 159 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; 160 unsigned int i; 161 struct ifreq ifr; 162 unsigned int stats_sz = xstats_ctrl->stats_n * sizeof(uint64_t); 163 unsigned char et_stat_buf[sizeof(struct ethtool_stats) + stats_sz]; 164 struct ethtool_stats *et_stats = (struct ethtool_stats *)et_stat_buf; 165 166 et_stats->cmd = ETHTOOL_GSTATS; 167 et_stats->n_stats = xstats_ctrl->stats_n; 168 ifr.ifr_data = (caddr_t)et_stats; 169 if (priv_ifreq(priv, SIOCETHTOOL, &ifr) != 0) { 170 WARN("unable to read statistic values from device"); 171 return -1; 172 } 173 for (i = 0; i != xstats_n; ++i) { 174 if (priv_is_ib_cntr(mlx5_counters_init[i].ctr_name)) 175 priv_get_cntr_sysfs(priv, 176 mlx5_counters_init[i].ctr_name, 177 &stats[i]); 178 else 179 stats[i] = (uint64_t) 180 et_stats->data[xstats_ctrl->dev_table_idx[i]]; 181 } 182 return 0; 183 } 184 185 /** 186 * Query the number of statistics provided by ETHTOOL. 187 * 188 * @param priv 189 * Pointer to private structure. 190 * 191 * @return 192 * Number of statistics on success, -1 on error. 193 */ 194 static int 195 priv_ethtool_get_stats_n(struct priv *priv) { 196 struct ethtool_drvinfo drvinfo; 197 struct ifreq ifr; 198 199 drvinfo.cmd = ETHTOOL_GDRVINFO; 200 ifr.ifr_data = (caddr_t)&drvinfo; 201 if (priv_ifreq(priv, SIOCETHTOOL, &ifr) != 0) { 202 WARN("unable to query number of statistics"); 203 return -1; 204 } 205 return drvinfo.n_stats; 206 } 207 208 /** 209 * Init the structures to read device counters. 210 * 211 * @param priv 212 * Pointer to private structure. 213 */ 214 void 215 priv_xstats_init(struct priv *priv) 216 { 217 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; 218 unsigned int i; 219 unsigned int j; 220 struct ifreq ifr; 221 struct ethtool_gstrings *strings = NULL; 222 unsigned int dev_stats_n; 223 unsigned int str_sz; 224 225 dev_stats_n = priv_ethtool_get_stats_n(priv); 226 if (dev_stats_n < 1) { 227 WARN("no extended statistics available"); 228 return; 229 } 230 xstats_ctrl->stats_n = dev_stats_n; 231 /* Allocate memory to grab stat names and values. */ 232 str_sz = dev_stats_n * ETH_GSTRING_LEN; 233 strings = (struct ethtool_gstrings *) 234 rte_malloc("xstats_strings", 235 str_sz + sizeof(struct ethtool_gstrings), 0); 236 if (!strings) { 237 WARN("unable to allocate memory for xstats"); 238 return; 239 } 240 strings->cmd = ETHTOOL_GSTRINGS; 241 strings->string_set = ETH_SS_STATS; 242 strings->len = dev_stats_n; 243 ifr.ifr_data = (caddr_t)strings; 244 if (priv_ifreq(priv, SIOCETHTOOL, &ifr) != 0) { 245 WARN("unable to get statistic names"); 246 goto free; 247 } 248 for (j = 0; j != xstats_n; ++j) 249 xstats_ctrl->dev_table_idx[j] = dev_stats_n; 250 for (i = 0; i != dev_stats_n; ++i) { 251 const char *curr_string = (const char *) 252 &strings->data[i * ETH_GSTRING_LEN]; 253 254 for (j = 0; j != xstats_n; ++j) { 255 if (!strcmp(mlx5_counters_init[j].ctr_name, 256 curr_string)) { 257 xstats_ctrl->dev_table_idx[j] = i; 258 break; 259 } 260 } 261 } 262 for (j = 0; j != xstats_n; ++j) { 263 if (priv_is_ib_cntr(mlx5_counters_init[j].ctr_name)) 264 continue; 265 if (xstats_ctrl->dev_table_idx[j] >= dev_stats_n) { 266 WARN("counter \"%s\" is not recognized", 267 mlx5_counters_init[j].dpdk_name); 268 goto free; 269 } 270 } 271 /* Copy to base at first time. */ 272 assert(xstats_n <= MLX5_MAX_XSTATS); 273 priv_read_dev_counters(priv, xstats_ctrl->base); 274 free: 275 rte_free(strings); 276 } 277 278 /** 279 * Get device extended statistics. 280 * 281 * @param priv 282 * Pointer to private structure. 283 * @param[out] stats 284 * Pointer to rte extended stats table. 285 * 286 * @return 287 * Number of extended stats on success and stats is filled, 288 * negative on error. 289 */ 290 static int 291 priv_xstats_get(struct priv *priv, struct rte_eth_xstat *stats) 292 { 293 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; 294 unsigned int i; 295 unsigned int n = xstats_n; 296 uint64_t counters[n]; 297 298 if (priv_read_dev_counters(priv, counters) < 0) 299 return -1; 300 for (i = 0; i != xstats_n; ++i) { 301 stats[i].id = i; 302 stats[i].value = (counters[i] - xstats_ctrl->base[i]); 303 } 304 return n; 305 } 306 307 /** 308 * Reset device extended statistics. 309 * 310 * @param priv 311 * Pointer to private structure. 312 */ 313 static void 314 priv_xstats_reset(struct priv *priv) 315 { 316 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; 317 unsigned int i; 318 unsigned int n = xstats_n; 319 uint64_t counters[n]; 320 321 if (priv_read_dev_counters(priv, counters) < 0) 322 return; 323 for (i = 0; i != n; ++i) 324 xstats_ctrl->base[i] = counters[i]; 325 } 326 327 /** 328 * DPDK callback to get device statistics. 329 * 330 * @param dev 331 * Pointer to Ethernet device structure. 332 * @param[out] stats 333 * Stats structure output buffer. 334 */ 335 int 336 mlx5_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) 337 { 338 struct priv *priv = dev->data->dev_private; 339 struct rte_eth_stats tmp = {0}; 340 unsigned int i; 341 unsigned int idx; 342 343 priv_lock(priv); 344 /* Add software counters. */ 345 for (i = 0; (i != priv->rxqs_n); ++i) { 346 struct mlx5_rxq_data *rxq = (*priv->rxqs)[i]; 347 348 if (rxq == NULL) 349 continue; 350 idx = rxq->stats.idx; 351 if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) { 352 #ifdef MLX5_PMD_SOFT_COUNTERS 353 tmp.q_ipackets[idx] += rxq->stats.ipackets; 354 tmp.q_ibytes[idx] += rxq->stats.ibytes; 355 #endif 356 tmp.q_errors[idx] += (rxq->stats.idropped + 357 rxq->stats.rx_nombuf); 358 } 359 #ifdef MLX5_PMD_SOFT_COUNTERS 360 tmp.ipackets += rxq->stats.ipackets; 361 tmp.ibytes += rxq->stats.ibytes; 362 #endif 363 tmp.ierrors += rxq->stats.idropped; 364 tmp.rx_nombuf += rxq->stats.rx_nombuf; 365 } 366 for (i = 0; (i != priv->txqs_n); ++i) { 367 struct mlx5_txq_data *txq = (*priv->txqs)[i]; 368 369 if (txq == NULL) 370 continue; 371 idx = txq->stats.idx; 372 if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) { 373 #ifdef MLX5_PMD_SOFT_COUNTERS 374 tmp.q_opackets[idx] += txq->stats.opackets; 375 tmp.q_obytes[idx] += txq->stats.obytes; 376 #endif 377 tmp.q_errors[idx] += txq->stats.oerrors; 378 } 379 #ifdef MLX5_PMD_SOFT_COUNTERS 380 tmp.opackets += txq->stats.opackets; 381 tmp.obytes += txq->stats.obytes; 382 #endif 383 tmp.oerrors += txq->stats.oerrors; 384 } 385 #ifndef MLX5_PMD_SOFT_COUNTERS 386 /* FIXME: retrieve and add hardware counters. */ 387 #endif 388 *stats = tmp; 389 priv_unlock(priv); 390 return 0; 391 } 392 393 /** 394 * DPDK callback to clear device statistics. 395 * 396 * @param dev 397 * Pointer to Ethernet device structure. 398 */ 399 void 400 mlx5_stats_reset(struct rte_eth_dev *dev) 401 { 402 struct priv *priv = dev->data->dev_private; 403 unsigned int i; 404 unsigned int idx; 405 406 priv_lock(priv); 407 for (i = 0; (i != priv->rxqs_n); ++i) { 408 if ((*priv->rxqs)[i] == NULL) 409 continue; 410 idx = (*priv->rxqs)[i]->stats.idx; 411 (*priv->rxqs)[i]->stats = 412 (struct mlx5_rxq_stats){ .idx = idx }; 413 } 414 for (i = 0; (i != priv->txqs_n); ++i) { 415 if ((*priv->txqs)[i] == NULL) 416 continue; 417 idx = (*priv->txqs)[i]->stats.idx; 418 (*priv->txqs)[i]->stats = 419 (struct mlx5_txq_stats){ .idx = idx }; 420 } 421 #ifndef MLX5_PMD_SOFT_COUNTERS 422 /* FIXME: reset hardware counters. */ 423 #endif 424 priv_unlock(priv); 425 } 426 427 /** 428 * DPDK callback to get extended device statistics. 429 * 430 * @param dev 431 * Pointer to Ethernet device structure. 432 * @param[out] stats 433 * Stats table output buffer. 434 * @param n 435 * The size of the stats table. 436 * 437 * @return 438 * Number of xstats on success, negative on failure. 439 */ 440 int 441 mlx5_xstats_get(struct rte_eth_dev *dev, 442 struct rte_eth_xstat *stats, unsigned int n) 443 { 444 struct priv *priv = dev->data->dev_private; 445 int ret = xstats_n; 446 447 if (n >= xstats_n && stats) { 448 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; 449 int stats_n; 450 451 priv_lock(priv); 452 stats_n = priv_ethtool_get_stats_n(priv); 453 if (stats_n < 0) { 454 priv_unlock(priv); 455 return -1; 456 } 457 if (xstats_ctrl->stats_n != stats_n) 458 priv_xstats_init(priv); 459 ret = priv_xstats_get(priv, stats); 460 priv_unlock(priv); 461 } 462 return ret; 463 } 464 465 /** 466 * DPDK callback to clear device extended statistics. 467 * 468 * @param dev 469 * Pointer to Ethernet device structure. 470 */ 471 void 472 mlx5_xstats_reset(struct rte_eth_dev *dev) 473 { 474 struct priv *priv = dev->data->dev_private; 475 struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; 476 int stats_n; 477 478 priv_lock(priv); 479 stats_n = priv_ethtool_get_stats_n(priv); 480 if (stats_n < 0) 481 goto unlock; 482 if (xstats_ctrl->stats_n != stats_n) 483 priv_xstats_init(priv); 484 priv_xstats_reset(priv); 485 unlock: 486 priv_unlock(priv); 487 } 488 489 /** 490 * DPDK callback to retrieve names of extended device statistics 491 * 492 * @param dev 493 * Pointer to Ethernet device structure. 494 * @param[out] xstats_names 495 * Buffer to insert names into. 496 * @param n 497 * Number of names. 498 * 499 * @return 500 * Number of xstats names. 501 */ 502 int 503 mlx5_xstats_get_names(struct rte_eth_dev *dev, 504 struct rte_eth_xstat_name *xstats_names, unsigned int n) 505 { 506 struct priv *priv = dev->data->dev_private; 507 unsigned int i; 508 509 if (n >= xstats_n && xstats_names) { 510 priv_lock(priv); 511 for (i = 0; i != xstats_n; ++i) { 512 strncpy(xstats_names[i].name, 513 mlx5_counters_init[i].dpdk_name, 514 RTE_ETH_XSTATS_NAME_SIZE); 515 xstats_names[i].name[RTE_ETH_XSTATS_NAME_SIZE - 1] = 0; 516 } 517 priv_unlock(priv); 518 } 519 return xstats_n; 520 } 521