xref: /illumos-gate/usr/src/uts/common/io/nvme/nvme_stat.c (revision f5f0964ce91892f7482efc86903b0ec7c7b6ba66)
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