1*5a05d72dSAndy Fiddaman /*
2*5a05d72dSAndy Fiddaman * This file and its contents are supplied under the terms of the
3*5a05d72dSAndy Fiddaman * Common Development and Distribution License ("CDDL"), version 1.0.
4*5a05d72dSAndy Fiddaman * You may only use this file in accordance with the terms of version
5*5a05d72dSAndy Fiddaman * 1.0 of the CDDL.
6*5a05d72dSAndy Fiddaman *
7*5a05d72dSAndy Fiddaman * A full copy of the text of the CDDL should have accompanied this
8*5a05d72dSAndy Fiddaman * source. A copy of the CDDL is also available via the Internet at
9*5a05d72dSAndy Fiddaman * http://www.illumos.org/license/CDDL.
10*5a05d72dSAndy Fiddaman */
11*5a05d72dSAndy Fiddaman
12*5a05d72dSAndy Fiddaman /*
13*5a05d72dSAndy Fiddaman * Copyright 2024 Oxide Computer Company
14*5a05d72dSAndy Fiddaman */
15*5a05d72dSAndy Fiddaman
16*5a05d72dSAndy Fiddaman #include "nvme_reg.h"
17*5a05d72dSAndy Fiddaman #include "nvme_var.h"
18*5a05d72dSAndy Fiddaman
19*5a05d72dSAndy Fiddaman static void
nvme_stat_device_cleanup(nvme_t * nvme)20*5a05d72dSAndy Fiddaman nvme_stat_device_cleanup(nvme_t *nvme)
21*5a05d72dSAndy Fiddaman {
22*5a05d72dSAndy Fiddaman if (nvme->n_device_kstat != NULL) {
23*5a05d72dSAndy Fiddaman kstat_delete(nvme->n_device_kstat);
24*5a05d72dSAndy Fiddaman nvme->n_device_kstat = NULL;
25*5a05d72dSAndy Fiddaman }
26*5a05d72dSAndy Fiddaman }
27*5a05d72dSAndy Fiddaman
28*5a05d72dSAndy Fiddaman static boolean_t
nvme_stat_device_init(nvme_t * nvme)29*5a05d72dSAndy Fiddaman nvme_stat_device_init(nvme_t *nvme)
30*5a05d72dSAndy Fiddaman {
31*5a05d72dSAndy Fiddaman kstat_t *ksp = kstat_create(NVME_MODULE_NAME,
32*5a05d72dSAndy Fiddaman ddi_get_instance(nvme->n_dip), "device", "controller",
33*5a05d72dSAndy Fiddaman KSTAT_TYPE_NAMED,
34*5a05d72dSAndy Fiddaman sizeof (nvme_device_stat_t) / sizeof (kstat_named_t),
35*5a05d72dSAndy Fiddaman KSTAT_FLAG_VIRTUAL);
36*5a05d72dSAndy Fiddaman nvme_device_stat_t *nds = &nvme->n_device_stat;
37*5a05d72dSAndy Fiddaman
38*5a05d72dSAndy Fiddaman if (ksp == NULL) {
39*5a05d72dSAndy Fiddaman dev_err(nvme->n_dip, CE_WARN,
40*5a05d72dSAndy Fiddaman "!failed to create device kstats");
41*5a05d72dSAndy Fiddaman return (false);
42*5a05d72dSAndy Fiddaman }
43*5a05d72dSAndy Fiddaman
44*5a05d72dSAndy Fiddaman nvme->n_device_kstat = ksp;
45*5a05d72dSAndy Fiddaman ksp->ks_data = nds;
46*5a05d72dSAndy Fiddaman
47*5a05d72dSAndy Fiddaman #define STAT_INIT(stat) \
48*5a05d72dSAndy Fiddaman kstat_named_init(&nds->nds_ ## stat, #stat, KSTAT_DATA_UINT64)
49*5a05d72dSAndy Fiddaman
50*5a05d72dSAndy Fiddaman /* Errors detected by driver */
51*5a05d72dSAndy Fiddaman STAT_INIT(dma_bind_err);
52*5a05d72dSAndy Fiddaman STAT_INIT(abort_timeout);
53*5a05d72dSAndy Fiddaman STAT_INIT(abort_failed);
54*5a05d72dSAndy Fiddaman STAT_INIT(abort_successful);
55*5a05d72dSAndy Fiddaman STAT_INIT(abort_unsuccessful);
56*5a05d72dSAndy Fiddaman STAT_INIT(cmd_timeout);
57*5a05d72dSAndy Fiddaman STAT_INIT(wrong_logpage);
58*5a05d72dSAndy Fiddaman STAT_INIT(unknown_logpage);
59*5a05d72dSAndy Fiddaman STAT_INIT(too_many_cookies);
60*5a05d72dSAndy Fiddaman STAT_INIT(unknown_cid);
61*5a05d72dSAndy Fiddaman
62*5a05d72dSAndy Fiddaman /* Errors detected by hardware */
63*5a05d72dSAndy Fiddaman STAT_INIT(inv_cmd_err);
64*5a05d72dSAndy Fiddaman STAT_INIT(inv_field_err);
65*5a05d72dSAndy Fiddaman STAT_INIT(inv_nsfmt_err);
66*5a05d72dSAndy Fiddaman STAT_INIT(data_xfr_err);
67*5a05d72dSAndy Fiddaman STAT_INIT(internal_err);
68*5a05d72dSAndy Fiddaman STAT_INIT(abort_rq_err);
69*5a05d72dSAndy Fiddaman STAT_INIT(abort_pwrloss_err);
70*5a05d72dSAndy Fiddaman STAT_INIT(abort_sq_del);
71*5a05d72dSAndy Fiddaman STAT_INIT(nvm_cap_exc);
72*5a05d72dSAndy Fiddaman STAT_INIT(nvm_ns_notrdy);
73*5a05d72dSAndy Fiddaman STAT_INIT(nvm_ns_formatting);
74*5a05d72dSAndy Fiddaman STAT_INIT(inv_cq_err);
75*5a05d72dSAndy Fiddaman STAT_INIT(inv_qid_err);
76*5a05d72dSAndy Fiddaman STAT_INIT(max_qsz_exc);
77*5a05d72dSAndy Fiddaman STAT_INIT(inv_int_vect);
78*5a05d72dSAndy Fiddaman STAT_INIT(inv_log_page);
79*5a05d72dSAndy Fiddaman STAT_INIT(inv_format);
80*5a05d72dSAndy Fiddaman STAT_INIT(inv_q_del);
81*5a05d72dSAndy Fiddaman STAT_INIT(cnfl_attr);
82*5a05d72dSAndy Fiddaman STAT_INIT(inv_prot);
83*5a05d72dSAndy Fiddaman STAT_INIT(readonly);
84*5a05d72dSAndy Fiddaman STAT_INIT(inv_fwslot);
85*5a05d72dSAndy Fiddaman STAT_INIT(inv_fwimg);
86*5a05d72dSAndy Fiddaman STAT_INIT(fwact_creset);
87*5a05d72dSAndy Fiddaman STAT_INIT(fwact_nssr);
88*5a05d72dSAndy Fiddaman STAT_INIT(fwact_reset);
89*5a05d72dSAndy Fiddaman STAT_INIT(fwact_mtfa);
90*5a05d72dSAndy Fiddaman STAT_INIT(fwact_prohibited);
91*5a05d72dSAndy Fiddaman STAT_INIT(fw_overlap);
92*5a05d72dSAndy Fiddaman
93*5a05d72dSAndy Fiddaman /* Errors reported by asynchronous events */
94*5a05d72dSAndy Fiddaman STAT_INIT(diagfail_event);
95*5a05d72dSAndy Fiddaman STAT_INIT(persistent_event);
96*5a05d72dSAndy Fiddaman STAT_INIT(transient_event);
97*5a05d72dSAndy Fiddaman STAT_INIT(fw_load_event);
98*5a05d72dSAndy Fiddaman STAT_INIT(reliability_event);
99*5a05d72dSAndy Fiddaman STAT_INIT(temperature_event);
100*5a05d72dSAndy Fiddaman STAT_INIT(spare_event);
101*5a05d72dSAndy Fiddaman STAT_INIT(vendor_event);
102*5a05d72dSAndy Fiddaman STAT_INIT(notice_event);
103*5a05d72dSAndy Fiddaman STAT_INIT(unknown_event);
104*5a05d72dSAndy Fiddaman
105*5a05d72dSAndy Fiddaman #undef STAT_INIT
106*5a05d72dSAndy Fiddaman
107*5a05d72dSAndy Fiddaman kstat_install(nvme->n_device_kstat);
108*5a05d72dSAndy Fiddaman return (true);
109*5a05d72dSAndy Fiddaman }
110*5a05d72dSAndy Fiddaman
111*5a05d72dSAndy Fiddaman static void
nvme_stat_admin_cleanup(nvme_t * nvme)112*5a05d72dSAndy Fiddaman nvme_stat_admin_cleanup(nvme_t *nvme)
113*5a05d72dSAndy Fiddaman {
114*5a05d72dSAndy Fiddaman if (nvme->n_admin_kstat != NULL) {
115*5a05d72dSAndy Fiddaman kstat_delete(nvme->n_admin_kstat);
116*5a05d72dSAndy Fiddaman nvme->n_admin_kstat = NULL;
117*5a05d72dSAndy Fiddaman mutex_destroy(&nvme->n_admin_stat_mutex);
118*5a05d72dSAndy Fiddaman }
119*5a05d72dSAndy Fiddaman }
120*5a05d72dSAndy Fiddaman
121*5a05d72dSAndy Fiddaman static boolean_t
nvme_stat_admin_init(nvme_t * nvme)122*5a05d72dSAndy Fiddaman nvme_stat_admin_init(nvme_t *nvme)
123*5a05d72dSAndy Fiddaman {
124*5a05d72dSAndy Fiddaman kstat_t *ksp = kstat_create(NVME_MODULE_NAME,
125*5a05d72dSAndy Fiddaman ddi_get_instance(nvme->n_dip), "admin", "controller",
126*5a05d72dSAndy Fiddaman KSTAT_TYPE_NAMED,
127*5a05d72dSAndy Fiddaman sizeof (nvme_admin_stat_t) / sizeof (kstat_named_t),
128*5a05d72dSAndy Fiddaman KSTAT_FLAG_VIRTUAL);
129*5a05d72dSAndy Fiddaman nvme_admin_stat_t *nas = &nvme->n_admin_stat;
130*5a05d72dSAndy Fiddaman
131*5a05d72dSAndy Fiddaman if (ksp == NULL) {
132*5a05d72dSAndy Fiddaman dev_err(nvme->n_dip, CE_WARN,
133*5a05d72dSAndy Fiddaman "!failed to create admin kstats");
134*5a05d72dSAndy Fiddaman return (false);
135*5a05d72dSAndy Fiddaman }
136*5a05d72dSAndy Fiddaman
137*5a05d72dSAndy Fiddaman nvme->n_admin_kstat = ksp;
138*5a05d72dSAndy Fiddaman ksp->ks_data = nas;
139*5a05d72dSAndy Fiddaman mutex_init(&nvme->n_admin_stat_mutex, NULL, MUTEX_DRIVER, NULL);
140*5a05d72dSAndy Fiddaman
141*5a05d72dSAndy Fiddaman #define STAT_INIT_ONE(stat, index, postfix) \
142*5a05d72dSAndy Fiddaman kstat_named_init(&nas->nas_ ## stat[index], #stat postfix, \
143*5a05d72dSAndy Fiddaman KSTAT_DATA_UINT64)
144*5a05d72dSAndy Fiddaman
145*5a05d72dSAndy Fiddaman #define STAT_INIT(stat) \
146*5a05d72dSAndy Fiddaman do { \
147*5a05d72dSAndy Fiddaman STAT_INIT_ONE(stat, NAS_CNT, "_cnt"); \
148*5a05d72dSAndy Fiddaman STAT_INIT_ONE(stat, NAS_AVG, "_avg"); \
149*5a05d72dSAndy Fiddaman STAT_INIT_ONE(stat, NAS_MAX, "_max"); \
150*5a05d72dSAndy Fiddaman } while (0)
151*5a05d72dSAndy Fiddaman
152*5a05d72dSAndy Fiddaman STAT_INIT(getlogpage);
153*5a05d72dSAndy Fiddaman STAT_INIT(identify);
154*5a05d72dSAndy Fiddaman STAT_INIT(abort);
155*5a05d72dSAndy Fiddaman STAT_INIT(fwactivate);
156*5a05d72dSAndy Fiddaman STAT_INIT(fwimgload);
157*5a05d72dSAndy Fiddaman STAT_INIT(nsformat);
158*5a05d72dSAndy Fiddaman STAT_INIT(vendor);
159*5a05d72dSAndy Fiddaman STAT_INIT(other);
160*5a05d72dSAndy Fiddaman
161*5a05d72dSAndy Fiddaman #undef STAT_INIT
162*5a05d72dSAndy Fiddaman #undef STAT_INIT_ONE
163*5a05d72dSAndy Fiddaman
164*5a05d72dSAndy Fiddaman kstat_install(nvme->n_admin_kstat);
165*5a05d72dSAndy Fiddaman return (true);
166*5a05d72dSAndy Fiddaman }
167*5a05d72dSAndy Fiddaman
168*5a05d72dSAndy Fiddaman void
nvme_stat_cleanup(nvme_t * nvme)169*5a05d72dSAndy Fiddaman nvme_stat_cleanup(nvme_t *nvme)
170*5a05d72dSAndy Fiddaman {
171*5a05d72dSAndy Fiddaman nvme_stat_device_cleanup(nvme);
172*5a05d72dSAndy Fiddaman nvme_stat_admin_cleanup(nvme);
173*5a05d72dSAndy Fiddaman }
174*5a05d72dSAndy Fiddaman
175*5a05d72dSAndy Fiddaman boolean_t
nvme_stat_init(nvme_t * nvme)176*5a05d72dSAndy Fiddaman nvme_stat_init(nvme_t *nvme)
177*5a05d72dSAndy Fiddaman {
178*5a05d72dSAndy Fiddaman if (!nvme_stat_device_init(nvme) || !nvme_stat_admin_init(nvme)) {
179*5a05d72dSAndy Fiddaman nvme_stat_cleanup(nvme);
180*5a05d72dSAndy Fiddaman return (B_FALSE);
181*5a05d72dSAndy Fiddaman }
182*5a05d72dSAndy Fiddaman return (B_TRUE);
183*5a05d72dSAndy Fiddaman }
184*5a05d72dSAndy Fiddaman
185*5a05d72dSAndy Fiddaman void
nvme_admin_stat_cmd(nvme_t * nvme,nvme_cmd_t * cmd)186*5a05d72dSAndy Fiddaman nvme_admin_stat_cmd(nvme_t *nvme, nvme_cmd_t *cmd)
187*5a05d72dSAndy Fiddaman {
188*5a05d72dSAndy Fiddaman hrtime_t t;
189*5a05d72dSAndy Fiddaman uint64_t cnt, avg;
190*5a05d72dSAndy Fiddaman kstat_named_t *data, *cntd, *avgd, *maxd;
191*5a05d72dSAndy Fiddaman
192*5a05d72dSAndy Fiddaman switch (cmd->nc_sqe.sqe_opc) {
193*5a05d72dSAndy Fiddaman case NVME_OPC_DELETE_SQUEUE:
194*5a05d72dSAndy Fiddaman case NVME_OPC_CREATE_SQUEUE:
195*5a05d72dSAndy Fiddaman case NVME_OPC_DELETE_CQUEUE:
196*5a05d72dSAndy Fiddaman case NVME_OPC_CREATE_CQUEUE:
197*5a05d72dSAndy Fiddaman /* No statistics are kept for these opcodes */
198*5a05d72dSAndy Fiddaman return;
199*5a05d72dSAndy Fiddaman case NVME_OPC_GET_LOG_PAGE:
200*5a05d72dSAndy Fiddaman data = nvme->n_admin_stat.nas_getlogpage;
201*5a05d72dSAndy Fiddaman break;
202*5a05d72dSAndy Fiddaman case NVME_OPC_IDENTIFY:
203*5a05d72dSAndy Fiddaman data = nvme->n_admin_stat.nas_identify;
204*5a05d72dSAndy Fiddaman break;
205*5a05d72dSAndy Fiddaman case NVME_OPC_ABORT:
206*5a05d72dSAndy Fiddaman data = nvme->n_admin_stat.nas_abort;
207*5a05d72dSAndy Fiddaman break;
208*5a05d72dSAndy Fiddaman case NVME_OPC_FW_ACTIVATE:
209*5a05d72dSAndy Fiddaman data = nvme->n_admin_stat.nas_fwactivate;
210*5a05d72dSAndy Fiddaman break;
211*5a05d72dSAndy Fiddaman case NVME_OPC_FW_IMAGE_LOAD:
212*5a05d72dSAndy Fiddaman data = nvme->n_admin_stat.nas_fwimgload;
213*5a05d72dSAndy Fiddaman break;
214*5a05d72dSAndy Fiddaman case NVME_OPC_NVM_FORMAT:
215*5a05d72dSAndy Fiddaman data = nvme->n_admin_stat.nas_nsformat;
216*5a05d72dSAndy Fiddaman break;
217*5a05d72dSAndy Fiddaman case NVME_OPC_VENDOR_LOW ... NVME_OPC_VENDOR_HIGH:
218*5a05d72dSAndy Fiddaman data = nvme->n_admin_stat.nas_vendor;
219*5a05d72dSAndy Fiddaman break;
220*5a05d72dSAndy Fiddaman default:
221*5a05d72dSAndy Fiddaman data = nvme->n_admin_stat.nas_other;
222*5a05d72dSAndy Fiddaman break;
223*5a05d72dSAndy Fiddaman }
224*5a05d72dSAndy Fiddaman
225*5a05d72dSAndy Fiddaman t = gethrtime() - cmd->nc_submit_ts;
226*5a05d72dSAndy Fiddaman
227*5a05d72dSAndy Fiddaman cntd = &data[NAS_CNT];
228*5a05d72dSAndy Fiddaman avgd = &data[NAS_AVG];
229*5a05d72dSAndy Fiddaman maxd = &data[NAS_MAX];
230*5a05d72dSAndy Fiddaman
231*5a05d72dSAndy Fiddaman mutex_enter(&nvme->n_admin_stat_mutex);
232*5a05d72dSAndy Fiddaman cnt = cntd->value.ui64;
233*5a05d72dSAndy Fiddaman avg = avgd->value.ui64;
234*5a05d72dSAndy Fiddaman
235*5a05d72dSAndy Fiddaman /*
236*5a05d72dSAndy Fiddaman * Update the cumulative rolling average.
237*5a05d72dSAndy Fiddaman * Since `t` and `avg` are orders of magnitude greater than `cnt` it
238*5a05d72dSAndy Fiddaman * is sufficient to directly adjust the current average towards the
239*5a05d72dSAndy Fiddaman * new value.
240*5a05d72dSAndy Fiddaman */
241*5a05d72dSAndy Fiddaman if (t > avg)
242*5a05d72dSAndy Fiddaman avg += (t - avg) / (cnt + 1);
243*5a05d72dSAndy Fiddaman else
244*5a05d72dSAndy Fiddaman avg -= (avg - t) / (cnt + 1);
245*5a05d72dSAndy Fiddaman cntd->value.ui64++;
246*5a05d72dSAndy Fiddaman avgd->value.ui64 = avg;
247*5a05d72dSAndy Fiddaman if (t > maxd->value.ui64)
248*5a05d72dSAndy Fiddaman maxd->value.ui64 = t;
249*5a05d72dSAndy Fiddaman mutex_exit(&nvme->n_admin_stat_mutex);
250*5a05d72dSAndy Fiddaman }
251