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