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