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