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
ena_stat_device_cleanup(ena_t * ena)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
ena_stat_device_init(ena_t * ena)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
ena_stat_device_basic_update(kstat_t * ksp,int rw)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
ena_stat_device_basic_cleanup(ena_t * ena)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
ena_stat_device_basic_init(ena_t * ena)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
ena_stat_device_extended_update(kstat_t * ksp,int rw)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
ena_stat_device_extended_cleanup(ena_t * ena)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
ena_stat_device_extended_init(ena_t * ena)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
ena_stat_aenq_cleanup(ena_t * ena)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
ena_stat_aenq_init(ena_t * ena)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
ena_stat_txq_cleanup(ena_txq_t * txq)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
ena_stat_txq_init(ena_txq_t * txq)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
ena_stat_rxq_cleanup(ena_rxq_t * rxq)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
ena_stat_rxq_init(ena_rxq_t * rxq)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
ena_ring_rx_stat(mac_ring_driver_t rh,uint_t stat,uint64_t * val)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
ena_ring_tx_stat(mac_ring_driver_t rh,uint_t stat,uint64_t * val)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
ena_m_stat(void * arg,uint_t stat,uint64_t * val)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