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 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 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 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 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 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 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 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