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 2021 Oxide Computer Company 14 */ 15 #include "ena.h" 16 17 /* 18 * The ENA device provides the following hardware stats. It appears 19 * that all stats are available at both a device-level and 20 * queue-level. However, Linux and FreeBSD don't implement queue 21 * scope. It's not clear how one would implement queue scope because 22 * there is nothing in the common code describing how to determine the 23 * queue index number. Both the SQ and CQ have device index values, 24 * but for a given logical queue they don't always match and so it's 25 * not clear what value to use for querying the stats. Therefore, 26 * device-wide basic and extended stats come from the device, while 27 * queue/ring stats come from driver. 28 * 29 * From empirical testing, these statistics appear to be cumulative. 30 * However, this guarantee is not explicitly documented anywhere in 31 * the common code that the author could find. 32 * 33 * BASIC (ENAHW_GET_STATS_TYPE_BASIC) 34 * 35 * - Rx packets/bytes 36 * - Rx drops 37 * - Tx packets/bytes 38 * - Tx drops 39 * 40 * EXTENDED (ENAHW_GET_STATS_TYPE_EXTENDED) 41 * 42 * There is no structure defined for these stats in the Linux 43 * driver. Based on the FreeBSD driver, it looks like extended 44 * stats are simply a buffer of C strings? Come back to this 45 * later. 46 * 47 * ENI (ENAHW_GET_STATS_TYPE_ENI) 48 * 49 * - Rx Bandwidth Allowance Exceeded 50 * - Tx Bandwidth Allowance Exceeded 51 * - PPS Allowance Exceeded (presumably for combined Rx/Tx) 52 * - Connection Tracking PPS Allowance Exceeded 53 * - Link-local PPS Alloance Exceeded 54 */ 55 56 static int 57 ena_stat_device_basic_update(kstat_t *ksp, int rw) 58 { 59 ena_t *ena = ksp->ks_private; 60 ena_basic_stat_t *ebs = ksp->ks_data; 61 enahw_resp_desc_t resp; 62 enahw_resp_basic_stats_t *stats = &resp.erd_resp.erd_basic_stats; 63 int ret = 0; 64 65 if (rw == KSTAT_WRITE) { 66 return (EACCES); 67 } 68 69 if ((ret = ena_admin_get_basic_stats(ena, &resp)) != 0) { 70 return (ret); 71 } 72 73 mutex_enter(&ena->ena_lock); 74 75 ebs->ebs_tx_bytes.value.ui64 = 76 ((uint64_t)stats->erbs_tx_bytes_high << 32) | 77 (uint64_t)stats->erbs_tx_bytes_low; 78 ebs->ebs_tx_pkts.value.ui64 = 79 ((uint64_t)stats->erbs_tx_pkts_high << 32) | 80 (uint64_t)stats->erbs_tx_pkts_low; 81 ebs->ebs_tx_drops.value.ui64 = 82 ((uint64_t)stats->erbs_tx_drops_high << 32) | 83 (uint64_t)stats->erbs_tx_drops_low; 84 85 ebs->ebs_rx_bytes.value.ui64 = 86 ((uint64_t)stats->erbs_rx_bytes_high << 32) | 87 (uint64_t)stats->erbs_rx_bytes_low; 88 ebs->ebs_rx_pkts.value.ui64 = 89 ((uint64_t)stats->erbs_rx_pkts_high << 32) | 90 (uint64_t)stats->erbs_rx_pkts_low; 91 ebs->ebs_rx_drops.value.ui64 = 92 ((uint64_t)stats->erbs_rx_drops_high << 32) | 93 (uint64_t)stats->erbs_rx_drops_low; 94 95 mutex_exit(&ena->ena_lock); 96 97 return (0); 98 } 99 100 void 101 ena_stat_device_basic_cleanup(ena_t *ena) 102 { 103 if (ena->ena_device_basic_kstat != NULL) { 104 kstat_delete(ena->ena_device_basic_kstat); 105 ena->ena_device_basic_kstat = NULL; 106 } 107 } 108 109 boolean_t 110 ena_stat_device_basic_init(ena_t *ena) 111 { 112 kstat_t *ksp = kstat_create(ENA_MODULE_NAME, 113 ddi_get_instance(ena->ena_dip), "device_basic", "net", 114 KSTAT_TYPE_NAMED, 115 sizeof (ena_basic_stat_t) / sizeof (kstat_named_t), 0); 116 ena_basic_stat_t *ebs = NULL; 117 118 if (ksp == NULL) { 119 ena_err(ena, "!failed to create device_basic kstats"); 120 return (B_FALSE); 121 } 122 123 ena->ena_device_basic_kstat = ksp; 124 ebs = ksp->ks_data; 125 ksp->ks_update = ena_stat_device_basic_update; 126 ksp->ks_private = ena; 127 128 kstat_named_init(&ebs->ebs_tx_bytes, "tx_bytes", KSTAT_DATA_UINT64); 129 ebs->ebs_tx_bytes.value.ui64 = 0; 130 kstat_named_init(&ebs->ebs_tx_pkts, "tx_packets", KSTAT_DATA_UINT64); 131 ebs->ebs_tx_pkts.value.ui64 = 0; 132 kstat_named_init(&ebs->ebs_tx_drops, "tx_drops", KSTAT_DATA_UINT64); 133 ebs->ebs_tx_drops.value.ui64 = 0; 134 135 kstat_named_init(&ebs->ebs_rx_bytes, "rx_bytes", KSTAT_DATA_UINT64); 136 ebs->ebs_rx_bytes.value.ui64 = 0; 137 kstat_named_init(&ebs->ebs_rx_pkts, "rx_packets", KSTAT_DATA_UINT64); 138 ebs->ebs_rx_pkts.value.ui64 = 0; 139 kstat_named_init(&ebs->ebs_rx_drops, "rx_drops", KSTAT_DATA_UINT64); 140 ebs->ebs_rx_drops.value.ui64 = 0; 141 142 kstat_install(ena->ena_device_basic_kstat); 143 return (B_TRUE); 144 } 145 146 int 147 ena_stat_device_extended_update(kstat_t *ksp, int rw) 148 { 149 ena_t *ena = ksp->ks_private; 150 ena_extended_stat_t *ees = ksp->ks_data; 151 enahw_resp_desc_t resp; 152 enahw_resp_eni_stats_t *stats = &resp.erd_resp.erd_eni_stats; 153 int ret = 0; 154 155 if (rw == KSTAT_WRITE) { 156 return (EACCES); 157 } 158 159 if ((ret = ena_admin_get_eni_stats(ena, &resp)) != 0) { 160 return (ret); 161 } 162 163 mutex_enter(&ena->ena_lock); 164 165 ees->ees_bw_in_exceeded.value.ui64 = stats->eres_bw_in_exceeded; 166 ees->ees_bw_out_exceeded.value.ui64 = stats->eres_bw_out_exceeded; 167 ees->ees_pps_exceeded.value.ui64 = stats->eres_pps_exceeded; 168 ees->ees_conns_exceeded.value.ui64 = stats->eres_conns_exceeded; 169 ees->ees_linklocal_exceeded.value.ui64 = stats->eres_linklocal_exceeded; 170 171 mutex_exit(&ena->ena_lock); 172 173 return (0); 174 } 175 176 void 177 ena_stat_device_extended_cleanup(ena_t *ena) 178 { 179 if (ena->ena_device_extended_kstat != NULL) { 180 kstat_delete(ena->ena_device_extended_kstat); 181 ena->ena_device_extended_kstat = NULL; 182 } 183 } 184 185 boolean_t 186 ena_stat_device_extended_init(ena_t *ena) 187 { 188 kstat_t *ksp = kstat_create(ENA_MODULE_NAME, 189 ddi_get_instance(ena->ena_dip), "device_ext", "net", 190 KSTAT_TYPE_NAMED, 191 sizeof (ena_extended_stat_t) / sizeof (kstat_named_t), 0); 192 ena_extended_stat_t *ees; 193 194 if (ksp == NULL) { 195 ena_err(ena, "!failed to create device_ext kstats"); 196 return (B_FALSE); 197 } 198 199 ena->ena_device_extended_kstat = ksp; 200 ees = ksp->ks_data; 201 ksp->ks_update = ena_stat_device_extended_update; 202 ksp->ks_private = ena; 203 204 kstat_named_init(&ees->ees_bw_in_exceeded, "bw_in_exceeded", 205 KSTAT_DATA_UINT64); 206 ees->ees_bw_in_exceeded.value.ui64 = 0; 207 208 kstat_named_init(&ees->ees_bw_out_exceeded, "bw_out_exceeded", 209 KSTAT_DATA_UINT64); 210 ees->ees_bw_out_exceeded.value.ui64 = 0; 211 212 kstat_named_init(&ees->ees_pps_exceeded, "pps_exceeded", 213 KSTAT_DATA_UINT64); 214 ees->ees_pps_exceeded.value.ui64 = 0; 215 216 kstat_named_init(&ees->ees_conns_exceeded, "conns_exceeded", 217 KSTAT_DATA_UINT64); 218 ees->ees_conns_exceeded.value.ui64 = 0; 219 220 kstat_named_init(&ees->ees_linklocal_exceeded, "linklocal_exceeded", 221 KSTAT_DATA_UINT64); 222 ees->ees_linklocal_exceeded.value.ui64 = 0; 223 224 kstat_install(ena->ena_device_extended_kstat); 225 return (B_TRUE); 226 } 227 228 void 229 ena_stat_aenq_cleanup(ena_t *ena) 230 { 231 if (ena->ena_aenq_kstat != NULL) { 232 kstat_delete(ena->ena_aenq_kstat); 233 ena->ena_aenq_kstat = NULL; 234 } 235 } 236 237 boolean_t 238 ena_stat_aenq_init(ena_t *ena) 239 { 240 kstat_t *ksp = kstat_create(ENA_MODULE_NAME, 241 ddi_get_instance(ena->ena_dip), "aenq", "net", KSTAT_TYPE_NAMED, 242 sizeof (ena_aenq_stat_t) / sizeof (kstat_named_t), 243 KSTAT_FLAG_VIRTUAL); 244 ena_aenq_stat_t *eas = &ena->ena_aenq_stat; 245 246 if (ksp == NULL) { 247 ena_err(ena, "!failed to create aenq kstats"); 248 return (B_FALSE); 249 } 250 251 ena->ena_aenq_kstat = ksp; 252 ksp->ks_data = eas; 253 254 kstat_named_init(&eas->eaes_default, "default", KSTAT_DATA_UINT64); 255 eas->eaes_default.value.ui64 = 0; 256 257 kstat_named_init(&eas->eaes_link_change, "link_change", 258 KSTAT_DATA_UINT64); 259 eas->eaes_link_change.value.ui64 = 0; 260 261 kstat_install(ena->ena_aenq_kstat); 262 return (B_TRUE); 263 } 264 265 void 266 ena_stat_txq_cleanup(ena_txq_t *txq) 267 { 268 if (txq->et_kstat != NULL) { 269 kstat_delete(txq->et_kstat); 270 txq->et_kstat = NULL; 271 } 272 } 273 274 boolean_t 275 ena_stat_txq_init(ena_txq_t *txq) 276 { 277 ena_t *ena = txq->et_ena; 278 kstat_t *ksp; 279 char buf[128]; 280 ena_txq_stat_t *ets = &txq->et_stat; 281 282 (void) snprintf(buf, sizeof (buf), "txq_%d", txq->et_txqs_idx); 283 284 ksp = kstat_create(ENA_MODULE_NAME, ddi_get_instance(ena->ena_dip), buf, 285 "net", KSTAT_TYPE_NAMED, 286 sizeof (ena_txq_stat_t) / sizeof (kstat_named_t), 287 KSTAT_FLAG_VIRTUAL); 288 289 if (ksp == NULL) { 290 ena_err(ena, "!failed to create %s kstats", buf); 291 return (B_FALSE); 292 } 293 294 txq->et_kstat = ksp; 295 ksp->ks_data = ets; 296 297 kstat_named_init(&ets->ets_hck_meoifail, "meoi_fail", 298 KSTAT_DATA_UINT64); 299 ets->ets_hck_meoifail.value.ui64 = 0; 300 301 kstat_named_init(&ets->ets_blocked, "blocked", KSTAT_DATA_UINT64); 302 ets->ets_blocked.value.ui64 = 0; 303 304 kstat_named_init(&ets->ets_unblocked, "unblocked", KSTAT_DATA_UINT64); 305 ets->ets_unblocked.value.ui64 = 0; 306 307 kstat_named_init(&ets->ets_recycled, "recycled", KSTAT_DATA_UINT64); 308 ets->ets_recycled.value.ui64 = 0; 309 310 kstat_named_init(&ets->ets_bytes, "bytes", KSTAT_DATA_UINT64); 311 ets->ets_bytes.value.ui64 = 0; 312 313 kstat_named_init(&ets->ets_packets, "packets", KSTAT_DATA_UINT64); 314 ets->ets_packets.value.ui64 = 0; 315 316 kstat_install(txq->et_kstat); 317 return (B_TRUE); 318 } 319 320 void 321 ena_stat_rxq_cleanup(ena_rxq_t *rxq) 322 { 323 if (rxq->er_kstat != NULL) { 324 kstat_delete(rxq->er_kstat); 325 rxq->er_kstat = NULL; 326 } 327 } 328 329 boolean_t 330 ena_stat_rxq_init(ena_rxq_t *rxq) 331 { 332 ena_t *ena = rxq->er_ena; 333 kstat_t *ksp; 334 char buf[128]; 335 ena_rxq_stat_t *ers = &rxq->er_stat; 336 337 (void) snprintf(buf, sizeof (buf), "rxq_%d", rxq->er_rxqs_idx); 338 339 ksp = kstat_create(ENA_MODULE_NAME, ddi_get_instance(ena->ena_dip), buf, 340 "net", KSTAT_TYPE_NAMED, 341 sizeof (ena_rxq_stat_t) / sizeof (kstat_named_t), 342 KSTAT_FLAG_VIRTUAL); 343 344 if (ksp == NULL) { 345 ena_err(ena, "!failed to create %s kstats", buf); 346 return (B_FALSE); 347 } 348 349 rxq->er_kstat = ksp; 350 ksp->ks_data = ers; 351 352 kstat_named_init(&ers->ers_packets, "packets", KSTAT_DATA_UINT64); 353 ers->ers_packets.value.ui64 = 0; 354 355 kstat_named_init(&ers->ers_bytes, "bytes", KSTAT_DATA_UINT64); 356 ers->ers_bytes.value.ui64 = 0; 357 358 kstat_named_init(&ers->ers_multi_desc, "multi_desc", KSTAT_DATA_UINT64); 359 ers->ers_multi_desc.value.ui64 = 0; 360 361 kstat_named_init(&ers->ers_allocb_fail, "allocb_fail", 362 KSTAT_DATA_UINT64); 363 ers->ers_allocb_fail.value.ui64 = 0; 364 365 kstat_named_init(&ers->ers_intr_limit, "intr_limit", KSTAT_DATA_UINT64); 366 ers->ers_intr_limit.value.ui64 = 0; 367 368 kstat_named_init(&ers->ers_hck_ipv4_err, "hck_ipv4_err", 369 KSTAT_DATA_UINT64); 370 ers->ers_hck_ipv4_err.value.ui64 = 0; 371 372 kstat_named_init(&ers->ers_hck_l4_err, "hck_l4_err", KSTAT_DATA_UINT64); 373 ers->ers_hck_l4_err.value.ui64 = 0; 374 375 kstat_install(rxq->er_kstat); 376 return (B_TRUE); 377 } 378 379 int 380 ena_ring_rx_stat(mac_ring_driver_t rh, uint_t stat, uint64_t *val) 381 { 382 int ret = 0; 383 ena_rxq_t *rxq = (ena_rxq_t *)rh; 384 385 mutex_enter(&rxq->er_stat_lock); 386 387 switch (stat) { 388 case MAC_STAT_RBYTES: 389 *val = rxq->er_stat.ers_bytes.value.ui64; 390 break; 391 case MAC_STAT_IPACKETS: 392 *val = rxq->er_stat.ers_packets.value.ui64; 393 break; 394 default: 395 *val = 0; 396 ret = ENOTSUP; 397 } 398 399 mutex_exit(&rxq->er_stat_lock); 400 return (ret); 401 } 402 403 int 404 ena_ring_tx_stat(mac_ring_driver_t rh, uint_t stat, uint64_t *val) 405 { 406 int ret = 0; 407 ena_txq_t *txq = (ena_txq_t *)rh; 408 409 mutex_enter(&txq->et_stat_lock); 410 411 switch (stat) { 412 case MAC_STAT_OBYTES: 413 *val = txq->et_stat.ets_bytes.value.ui64; 414 break; 415 case MAC_STAT_OPACKETS: 416 *val = txq->et_stat.ets_packets.value.ui64; 417 break; 418 default: 419 *val = 0; 420 ret = ENOTSUP; 421 } 422 423 mutex_exit(&txq->et_stat_lock); 424 return (ret); 425 } 426 427 int 428 ena_m_stat(void *arg, uint_t stat, uint64_t *val) 429 { 430 ena_t *ena = arg; 431 ena_basic_stat_t *ebs = ena->ena_device_basic_kstat->ks_data; 432 int ret = 0; 433 434 ret = ena_stat_device_basic_update(ena->ena_device_basic_kstat, 435 KSTAT_READ); 436 437 if (ret != 0) { 438 return (ret); 439 } 440 441 mutex_enter(&ena->ena_lock); 442 443 /* 444 * The ENA device does not provide a lot of the stats that a 445 * traditional NIC device would. 446 */ 447 switch (stat) { 448 case MAC_STAT_NORCVBUF: 449 *val = ebs->ebs_rx_drops.value.ui64; 450 break; 451 452 case MAC_STAT_RBYTES: 453 *val = ebs->ebs_rx_bytes.value.ui64; 454 break; 455 456 case MAC_STAT_IPACKETS: 457 *val = ebs->ebs_rx_pkts.value.ui64; 458 break; 459 460 case MAC_STAT_OBYTES: 461 *val = ebs->ebs_tx_bytes.value.ui64; 462 break; 463 464 case MAC_STAT_OPACKETS: 465 *val = ebs->ebs_tx_pkts.value.ui64; 466 break; 467 468 default: 469 ret = ENOTSUP; 470 break; 471 } 472 473 mutex_exit(&ena->ena_lock); 474 return (ret); 475 } 476