1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2024 Oxide Computer Company 14 */ 15 16 #include "ena.h" 17 18 /* 19 * The ENA device provides the following hardware stats. It appears 20 * that all stats are available at both a device-level and 21 * queue-level. However, Linux and FreeBSD don't implement queue 22 * scope. It's not clear how one would implement queue scope because 23 * there is nothing in the common code describing how to determine the 24 * queue index number. Both the SQ and CQ have device index values, 25 * but for a given logical queue they don't always match and so it's 26 * not clear what value to use for querying the stats. Therefore, 27 * device-wide basic and extended stats come from the device, while 28 * queue/ring stats come from driver. 29 * 30 * From empirical testing, these statistics appear to be cumulative. 31 * However, this guarantee is not explicitly documented anywhere in 32 * the common code that the author could find. 33 * 34 * BASIC (ENAHW_GET_STATS_TYPE_BASIC) 35 * 36 * - Rx packets/bytes 37 * - Rx drops 38 * - Tx packets/bytes 39 * - Tx drops 40 * - Rx overruns 41 * 42 * EXTENDED (ENAHW_GET_STATS_TYPE_EXTENDED) 43 * 44 * There is no structure defined for these stats in the Linux 45 * driver. Based on the FreeBSD driver, it looks like extended 46 * stats are simply a buffer of C strings? Come back to this 47 * later. 48 * 49 * ENI (ENAHW_GET_STATS_TYPE_ENI) 50 * 51 * - Rx Bandwidth Allowance Exceeded 52 * - Tx Bandwidth Allowance Exceeded 53 * - PPS Allowance Exceeded (presumably for combined Rx/Tx) 54 * - Connection Tracking PPS Allowance Exceeded 55 * - Link-local PPS Allowance Exceeded 56 */ 57 58 void 59 ena_stat_device_cleanup(ena_t *ena) 60 { 61 if (ena->ena_device_kstat != NULL) { 62 kstat_delete(ena->ena_device_kstat); 63 ena->ena_device_kstat = NULL; 64 } 65 } 66 67 bool 68 ena_stat_device_init(ena_t *ena) 69 { 70 kstat_t *ksp = kstat_create(ENA_MODULE_NAME, 71 ddi_get_instance(ena->ena_dip), "device", "net", KSTAT_TYPE_NAMED, 72 sizeof (ena_device_stat_t) / sizeof (kstat_named_t), 73 KSTAT_FLAG_VIRTUAL); 74 ena_device_stat_t *eds = &ena->ena_device_stat; 75 76 if (ksp == NULL) { 77 ena_err(ena, "!failed to create device kstats"); 78 return (false); 79 } 80 81 ena->ena_device_kstat = ksp; 82 ksp->ks_data = eds; 83 84 kstat_named_init(&eds->eds_reset_forced, "reset_forced", 85 KSTAT_DATA_UINT64); 86 kstat_named_init(&eds->eds_reset_error, "reset_error", 87 KSTAT_DATA_UINT64); 88 kstat_named_init(&eds->eds_reset_fatal, "reset_fatal", 89 KSTAT_DATA_UINT64); 90 kstat_named_init(&eds->eds_reset_keepalive, "reset_keepalive", 91 KSTAT_DATA_UINT64); 92 kstat_named_init(&eds->eds_reset_txstall, "reset_txstall", 93 KSTAT_DATA_UINT64); 94 95 kstat_install(ena->ena_device_kstat); 96 return (true); 97 } 98 99 static int 100 ena_stat_device_basic_update(kstat_t *ksp, int rw) 101 { 102 ena_t *ena = ksp->ks_private; 103 ena_basic_stat_t *ebs = ksp->ks_data; 104 enahw_resp_desc_t resp; 105 enahw_resp_basic_stats_t *stats = &resp.erd_resp.erd_basic_stats; 106 bool fetch; 107 int ret = 0; 108 109 if (rw == KSTAT_WRITE) { 110 return (EACCES); 111 } 112 113 mutex_enter(&ena->ena_device_basic_stat_lock); 114 fetch = gethrtime() - ena->ena_device_basic_stat_last_update > 115 ENA_BASIC_STATS_MINIMUM_INTERVAL_NS; 116 mutex_exit(&ena->ena_device_basic_stat_lock); 117 118 if (!fetch) 119 return (0); 120 121 if ((ret = ena_admin_get_basic_stats(ena, &resp)) != 0) 122 return (ret); 123 124 mutex_enter(&ena->ena_device_basic_stat_lock); 125 ena->ena_device_basic_stat_last_update = gethrtime(); 126 127 ebs->ebs_tx_bytes.value.ui64 = 128 ((uint64_t)stats->erbs_tx_bytes_high << 32) | 129 (uint64_t)stats->erbs_tx_bytes_low; 130 ebs->ebs_tx_pkts.value.ui64 = 131 ((uint64_t)stats->erbs_tx_pkts_high << 32) | 132 (uint64_t)stats->erbs_tx_pkts_low; 133 ebs->ebs_tx_drops.value.ui64 = 134 ((uint64_t)stats->erbs_tx_drops_high << 32) | 135 (uint64_t)stats->erbs_tx_drops_low; 136 137 ebs->ebs_rx_bytes.value.ui64 = 138 ((uint64_t)stats->erbs_rx_bytes_high << 32) | 139 (uint64_t)stats->erbs_rx_bytes_low; 140 ebs->ebs_rx_pkts.value.ui64 = 141 ((uint64_t)stats->erbs_rx_pkts_high << 32) | 142 (uint64_t)stats->erbs_rx_pkts_low; 143 ebs->ebs_rx_drops.value.ui64 = 144 ((uint64_t)stats->erbs_rx_drops_high << 32) | 145 (uint64_t)stats->erbs_rx_drops_low; 146 ebs->ebs_rx_overruns.value.ui64 = 147 ((uint64_t)stats->erbs_rx_overruns_high << 32) | 148 (uint64_t)stats->erbs_rx_overruns_low; 149 150 mutex_exit(&ena->ena_device_basic_stat_lock); 151 152 return (0); 153 } 154 155 void 156 ena_stat_device_basic_cleanup(ena_t *ena) 157 { 158 if (ena->ena_device_basic_kstat != NULL) { 159 mutex_destroy(&ena->ena_device_basic_stat_lock); 160 kstat_delete(ena->ena_device_basic_kstat); 161 ena->ena_device_basic_kstat = NULL; 162 } 163 } 164 165 bool 166 ena_stat_device_basic_init(ena_t *ena) 167 { 168 kstat_t *ksp = kstat_create(ENA_MODULE_NAME, 169 ddi_get_instance(ena->ena_dip), "device_basic", "net", 170 KSTAT_TYPE_NAMED, 171 sizeof (ena_basic_stat_t) / sizeof (kstat_named_t), 0); 172 ena_basic_stat_t *ebs = NULL; 173 174 if (ksp == NULL) { 175 ena_err(ena, "!failed to create device_basic kstats"); 176 return (false); 177 } 178 179 mutex_init(&ena->ena_device_basic_stat_lock, NULL, MUTEX_DRIVER, 180 DDI_INTR_PRI(ena->ena_intr_pri)); 181 ena->ena_device_basic_stat_last_update = 0; 182 183 ena->ena_device_basic_kstat = ksp; 184 ebs = ksp->ks_data; 185 ksp->ks_update = ena_stat_device_basic_update; 186 ksp->ks_private = ena; 187 188 kstat_named_init(&ebs->ebs_tx_bytes, "tx_bytes", KSTAT_DATA_UINT64); 189 ebs->ebs_tx_bytes.value.ui64 = 0; 190 kstat_named_init(&ebs->ebs_tx_pkts, "tx_packets", KSTAT_DATA_UINT64); 191 ebs->ebs_tx_pkts.value.ui64 = 0; 192 kstat_named_init(&ebs->ebs_tx_drops, "tx_drops", KSTAT_DATA_UINT64); 193 ebs->ebs_tx_drops.value.ui64 = 0; 194 195 kstat_named_init(&ebs->ebs_rx_bytes, "rx_bytes", KSTAT_DATA_UINT64); 196 ebs->ebs_rx_bytes.value.ui64 = 0; 197 kstat_named_init(&ebs->ebs_rx_pkts, "rx_packets", KSTAT_DATA_UINT64); 198 ebs->ebs_rx_pkts.value.ui64 = 0; 199 kstat_named_init(&ebs->ebs_rx_drops, "rx_drops", KSTAT_DATA_UINT64); 200 ebs->ebs_rx_drops.value.ui64 = 0; 201 kstat_named_init(&ebs->ebs_rx_overruns, "rx_overruns", 202 KSTAT_DATA_UINT64); 203 ebs->ebs_rx_overruns.value.ui64 = 0; 204 205 kstat_install(ena->ena_device_basic_kstat); 206 return (true); 207 } 208 209 static int 210 ena_stat_device_extended_update(kstat_t *ksp, int rw) 211 { 212 ena_t *ena = ksp->ks_private; 213 ena_extended_stat_t *ees = ksp->ks_data; 214 enahw_resp_desc_t resp; 215 enahw_resp_eni_stats_t *stats = &resp.erd_resp.erd_eni_stats; 216 int ret = 0; 217 218 if (rw == KSTAT_WRITE) { 219 return (EACCES); 220 } 221 222 if ((ret = ena_admin_get_eni_stats(ena, &resp)) != 0) { 223 return (ret); 224 } 225 226 mutex_enter(&ena->ena_lock); 227 228 ees->ees_bw_in_exceeded.value.ui64 = stats->eres_bw_in_exceeded; 229 ees->ees_bw_out_exceeded.value.ui64 = stats->eres_bw_out_exceeded; 230 ees->ees_pps_exceeded.value.ui64 = stats->eres_pps_exceeded; 231 ees->ees_conns_exceeded.value.ui64 = stats->eres_conns_exceeded; 232 ees->ees_linklocal_exceeded.value.ui64 = stats->eres_linklocal_exceeded; 233 234 mutex_exit(&ena->ena_lock); 235 236 return (0); 237 } 238 239 void 240 ena_stat_device_extended_cleanup(ena_t *ena) 241 { 242 if (ena->ena_device_extended_kstat != NULL) { 243 kstat_delete(ena->ena_device_extended_kstat); 244 ena->ena_device_extended_kstat = NULL; 245 } 246 } 247 248 bool 249 ena_stat_device_extended_init(ena_t *ena) 250 { 251 kstat_t *ksp = kstat_create(ENA_MODULE_NAME, 252 ddi_get_instance(ena->ena_dip), "device_ext", "net", 253 KSTAT_TYPE_NAMED, 254 sizeof (ena_extended_stat_t) / sizeof (kstat_named_t), 0); 255 ena_extended_stat_t *ees; 256 257 if (ksp == NULL) { 258 ena_err(ena, "!failed to create device_ext kstats"); 259 return (false); 260 } 261 262 ena->ena_device_extended_kstat = ksp; 263 ees = ksp->ks_data; 264 ksp->ks_update = ena_stat_device_extended_update; 265 ksp->ks_private = ena; 266 267 kstat_named_init(&ees->ees_bw_in_exceeded, "bw_in_exceeded", 268 KSTAT_DATA_UINT64); 269 ees->ees_bw_in_exceeded.value.ui64 = 0; 270 271 kstat_named_init(&ees->ees_bw_out_exceeded, "bw_out_exceeded", 272 KSTAT_DATA_UINT64); 273 ees->ees_bw_out_exceeded.value.ui64 = 0; 274 275 kstat_named_init(&ees->ees_pps_exceeded, "pps_exceeded", 276 KSTAT_DATA_UINT64); 277 ees->ees_pps_exceeded.value.ui64 = 0; 278 279 kstat_named_init(&ees->ees_conns_exceeded, "conns_exceeded", 280 KSTAT_DATA_UINT64); 281 ees->ees_conns_exceeded.value.ui64 = 0; 282 283 kstat_named_init(&ees->ees_linklocal_exceeded, "linklocal_exceeded", 284 KSTAT_DATA_UINT64); 285 ees->ees_linklocal_exceeded.value.ui64 = 0; 286 287 kstat_install(ena->ena_device_extended_kstat); 288 return (true); 289 } 290 291 void 292 ena_stat_aenq_cleanup(ena_t *ena) 293 { 294 if (ena->ena_aenq_kstat != NULL) { 295 kstat_delete(ena->ena_aenq_kstat); 296 ena->ena_aenq_kstat = NULL; 297 } 298 } 299 300 bool 301 ena_stat_aenq_init(ena_t *ena) 302 { 303 kstat_t *ksp = kstat_create(ENA_MODULE_NAME, 304 ddi_get_instance(ena->ena_dip), "aenq", "net", KSTAT_TYPE_NAMED, 305 sizeof (ena_aenq_stat_t) / sizeof (kstat_named_t), 306 KSTAT_FLAG_VIRTUAL); 307 ena_aenq_stat_t *eas = &ena->ena_aenq_stat; 308 309 if (ksp == NULL) { 310 ena_err(ena, "!failed to create aenq kstats"); 311 return (false); 312 } 313 314 ena->ena_aenq_kstat = ksp; 315 ksp->ks_data = eas; 316 317 kstat_named_init(&eas->eaes_default, "default", KSTAT_DATA_UINT64); 318 eas->eaes_default.value.ui64 = 0; 319 320 kstat_named_init(&eas->eaes_link_change, "link_change", 321 KSTAT_DATA_UINT64); 322 eas->eaes_link_change.value.ui64 = 0; 323 324 kstat_named_init(&eas->eaes_notification, "notification", 325 KSTAT_DATA_UINT64); 326 eas->eaes_notification.value.ui64 = 0; 327 328 kstat_named_init(&eas->eaes_keep_alive, "keep_alive", 329 KSTAT_DATA_UINT64); 330 eas->eaes_keep_alive.value.ui64 = 0; 331 332 kstat_named_init(&eas->eaes_request_reset, "request_reset", 333 KSTAT_DATA_UINT64); 334 eas->eaes_request_reset.value.ui64 = 0; 335 336 kstat_named_init(&eas->eaes_fatal_error, "fatal_error", 337 KSTAT_DATA_UINT64); 338 eas->eaes_fatal_error.value.ui64 = 0; 339 340 kstat_named_init(&eas->eaes_warning, "warning", KSTAT_DATA_UINT64); 341 eas->eaes_warning.value.ui64 = 0; 342 343 kstat_install(ena->ena_aenq_kstat); 344 return (true); 345 } 346 347 void 348 ena_stat_txq_cleanup(ena_txq_t *txq) 349 { 350 if (txq->et_kstat != NULL) { 351 kstat_delete(txq->et_kstat); 352 txq->et_kstat = NULL; 353 } 354 } 355 356 bool 357 ena_stat_txq_init(ena_txq_t *txq) 358 { 359 ena_t *ena = txq->et_ena; 360 kstat_t *ksp; 361 char buf[128]; 362 ena_txq_stat_t *ets = &txq->et_stat; 363 364 (void) snprintf(buf, sizeof (buf), "txq_%d", txq->et_txqs_idx); 365 366 ksp = kstat_create(ENA_MODULE_NAME, ddi_get_instance(ena->ena_dip), buf, 367 "net", KSTAT_TYPE_NAMED, 368 sizeof (ena_txq_stat_t) / sizeof (kstat_named_t), 369 KSTAT_FLAG_VIRTUAL); 370 371 if (ksp == NULL) { 372 ena_err(ena, "!failed to create %s kstats", buf); 373 return (false); 374 } 375 376 txq->et_kstat = ksp; 377 ksp->ks_data = ets; 378 379 kstat_named_init(&ets->ets_hck_meoifail, "meoi_fail", 380 KSTAT_DATA_UINT64); 381 ets->ets_hck_meoifail.value.ui64 = 0; 382 383 kstat_named_init(&ets->ets_blocked, "blocked", KSTAT_DATA_UINT64); 384 ets->ets_blocked.value.ui64 = 0; 385 386 kstat_named_init(&ets->ets_unblocked, "unblocked", KSTAT_DATA_UINT64); 387 ets->ets_unblocked.value.ui64 = 0; 388 389 kstat_named_init(&ets->ets_recycled, "recycled", KSTAT_DATA_UINT64); 390 ets->ets_recycled.value.ui64 = 0; 391 392 kstat_named_init(&ets->ets_bytes, "bytes", KSTAT_DATA_UINT64); 393 ets->ets_bytes.value.ui64 = 0; 394 395 kstat_named_init(&ets->ets_packets, "packets", KSTAT_DATA_UINT64); 396 ets->ets_packets.value.ui64 = 0; 397 398 kstat_install(txq->et_kstat); 399 return (true); 400 } 401 402 void 403 ena_stat_rxq_cleanup(ena_rxq_t *rxq) 404 { 405 if (rxq->er_kstat != NULL) { 406 kstat_delete(rxq->er_kstat); 407 rxq->er_kstat = NULL; 408 } 409 } 410 411 bool 412 ena_stat_rxq_init(ena_rxq_t *rxq) 413 { 414 ena_t *ena = rxq->er_ena; 415 kstat_t *ksp; 416 char buf[128]; 417 ena_rxq_stat_t *ers = &rxq->er_stat; 418 419 (void) snprintf(buf, sizeof (buf), "rxq_%d", rxq->er_rxqs_idx); 420 421 ksp = kstat_create(ENA_MODULE_NAME, ddi_get_instance(ena->ena_dip), buf, 422 "net", KSTAT_TYPE_NAMED, 423 sizeof (ena_rxq_stat_t) / sizeof (kstat_named_t), 424 KSTAT_FLAG_VIRTUAL); 425 426 if (ksp == NULL) { 427 ena_err(ena, "!failed to create %s kstats", buf); 428 return (false); 429 } 430 431 rxq->er_kstat = ksp; 432 ksp->ks_data = ers; 433 434 kstat_named_init(&ers->ers_packets, "packets", KSTAT_DATA_UINT64); 435 ers->ers_packets.value.ui64 = 0; 436 437 kstat_named_init(&ers->ers_bytes, "bytes", KSTAT_DATA_UINT64); 438 ers->ers_bytes.value.ui64 = 0; 439 440 kstat_named_init(&ers->ers_multi_desc, "multi_desc", KSTAT_DATA_UINT64); 441 ers->ers_multi_desc.value.ui64 = 0; 442 443 kstat_named_init(&ers->ers_allocb_fail, "allocb_fail", 444 KSTAT_DATA_UINT64); 445 ers->ers_allocb_fail.value.ui64 = 0; 446 447 kstat_named_init(&ers->ers_intr_limit, "intr_limit", KSTAT_DATA_UINT64); 448 ers->ers_intr_limit.value.ui64 = 0; 449 450 kstat_named_init(&ers->ers_hck_ipv4_err, "hck_ipv4_err", 451 KSTAT_DATA_UINT64); 452 ers->ers_hck_ipv4_err.value.ui64 = 0; 453 454 kstat_named_init(&ers->ers_hck_l4_err, "hck_l4_err", KSTAT_DATA_UINT64); 455 ers->ers_hck_l4_err.value.ui64 = 0; 456 457 kstat_install(rxq->er_kstat); 458 return (true); 459 } 460 461 int 462 ena_ring_rx_stat(mac_ring_driver_t rh, uint_t stat, uint64_t *val) 463 { 464 int ret = 0; 465 ena_rxq_t *rxq = (ena_rxq_t *)rh; 466 467 mutex_enter(&rxq->er_stat_lock); 468 469 switch (stat) { 470 case MAC_STAT_RBYTES: 471 *val = rxq->er_stat.ers_bytes.value.ui64; 472 break; 473 case MAC_STAT_IPACKETS: 474 *val = rxq->er_stat.ers_packets.value.ui64; 475 break; 476 default: 477 *val = 0; 478 ret = ENOTSUP; 479 } 480 481 mutex_exit(&rxq->er_stat_lock); 482 return (ret); 483 } 484 485 int 486 ena_ring_tx_stat(mac_ring_driver_t rh, uint_t stat, uint64_t *val) 487 { 488 int ret = 0; 489 ena_txq_t *txq = (ena_txq_t *)rh; 490 491 mutex_enter(&txq->et_stat_lock); 492 493 switch (stat) { 494 case MAC_STAT_OBYTES: 495 *val = txq->et_stat.ets_bytes.value.ui64; 496 break; 497 case MAC_STAT_OPACKETS: 498 *val = txq->et_stat.ets_packets.value.ui64; 499 break; 500 default: 501 *val = 0; 502 ret = ENOTSUP; 503 } 504 505 mutex_exit(&txq->et_stat_lock); 506 return (ret); 507 } 508 509 int 510 ena_m_stat(void *arg, uint_t stat, uint64_t *val) 511 { 512 ena_t *ena = arg; 513 ena_basic_stat_t *ebs; 514 int ret = 0; 515 bool fetch = false; 516 517 /* 518 * The ENA device does not provide a lot of the stats that a 519 * traditional NIC device would. Return ENOTSUP early for any we don't 520 * support, and avoid a round trip to the controller. 521 */ 522 switch (stat) { 523 case ETHER_STAT_LINK_DUPLEX: 524 case MAC_STAT_IFSPEED: 525 break; 526 case MAC_STAT_IERRORS: 527 case MAC_STAT_OERRORS: 528 case MAC_STAT_NORCVBUF: 529 case MAC_STAT_RBYTES: 530 case MAC_STAT_IPACKETS: 531 case MAC_STAT_OBYTES: 532 case MAC_STAT_OPACKETS: 533 fetch = true; 534 break; 535 default: 536 return (ENOTSUP); 537 } 538 539 if (fetch) { 540 ret = ena_stat_device_basic_update(ena->ena_device_basic_kstat, 541 KSTAT_READ); 542 543 if (ret != 0) 544 return (ret); 545 } 546 547 mutex_enter(&ena->ena_device_basic_stat_lock); 548 ebs = ena->ena_device_basic_kstat->ks_data; 549 550 switch (stat) { 551 case ETHER_STAT_LINK_DUPLEX: 552 *val = ena->ena_link_duplex; 553 break; 554 555 case MAC_STAT_IFSPEED: 556 *val = ena->ena_link_speed_mbits * 1000000ULL; 557 break; 558 559 case MAC_STAT_NORCVBUF: 560 *val = ebs->ebs_rx_overruns.value.ui64; 561 break; 562 563 case MAC_STAT_RBYTES: 564 *val = ebs->ebs_rx_bytes.value.ui64; 565 break; 566 567 case MAC_STAT_IPACKETS: 568 *val = ebs->ebs_rx_pkts.value.ui64; 569 break; 570 571 case MAC_STAT_IERRORS: 572 *val = ebs->ebs_rx_drops.value.ui64; 573 break; 574 575 case MAC_STAT_OBYTES: 576 *val = ebs->ebs_tx_bytes.value.ui64; 577 break; 578 579 case MAC_STAT_OPACKETS: 580 *val = ebs->ebs_tx_pkts.value.ui64; 581 break; 582 583 case MAC_STAT_OERRORS: 584 *val = ebs->ebs_tx_drops.value.ui64; 585 break; 586 587 default: 588 dev_err(ena->ena_dip, CE_PANIC, "unhandled stat, 0x%x", stat); 589 } 590 591 mutex_exit(&ena->ena_device_basic_stat_lock); 592 return (ret); 593 } 594