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 2024 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 93 /* Errors reported by asynchronous events */ 94 STAT_INIT(diagfail_event); 95 STAT_INIT(persistent_event); 96 STAT_INIT(transient_event); 97 STAT_INIT(fw_load_event); 98 STAT_INIT(reliability_event); 99 STAT_INIT(temperature_event); 100 STAT_INIT(spare_event); 101 STAT_INIT(vendor_event); 102 STAT_INIT(notice_event); 103 STAT_INIT(unknown_event); 104 105 #undef STAT_INIT 106 107 kstat_install(nvme->n_device_kstat); 108 return (true); 109 } 110 111 static void 112 nvme_stat_admin_cleanup(nvme_t *nvme) 113 { 114 if (nvme->n_admin_kstat != NULL) { 115 kstat_delete(nvme->n_admin_kstat); 116 nvme->n_admin_kstat = NULL; 117 mutex_destroy(&nvme->n_admin_stat_mutex); 118 } 119 } 120 121 static boolean_t 122 nvme_stat_admin_init(nvme_t *nvme) 123 { 124 kstat_t *ksp = kstat_create(NVME_MODULE_NAME, 125 ddi_get_instance(nvme->n_dip), "admin", "controller", 126 KSTAT_TYPE_NAMED, 127 sizeof (nvme_admin_stat_t) / sizeof (kstat_named_t), 128 KSTAT_FLAG_VIRTUAL); 129 nvme_admin_stat_t *nas = &nvme->n_admin_stat; 130 131 if (ksp == NULL) { 132 dev_err(nvme->n_dip, CE_WARN, 133 "!failed to create admin kstats"); 134 return (false); 135 } 136 137 nvme->n_admin_kstat = ksp; 138 ksp->ks_data = nas; 139 mutex_init(&nvme->n_admin_stat_mutex, NULL, MUTEX_DRIVER, NULL); 140 141 #define STAT_INIT_ONE(stat, index, postfix) \ 142 kstat_named_init(&nas->nas_ ## stat[index], #stat postfix, \ 143 KSTAT_DATA_UINT64) 144 145 #define STAT_INIT(stat) \ 146 do { \ 147 STAT_INIT_ONE(stat, NAS_CNT, "_cnt"); \ 148 STAT_INIT_ONE(stat, NAS_AVG, "_avg"); \ 149 STAT_INIT_ONE(stat, NAS_MAX, "_max"); \ 150 } while (0) 151 152 STAT_INIT(getlogpage); 153 STAT_INIT(identify); 154 STAT_INIT(abort); 155 STAT_INIT(fwactivate); 156 STAT_INIT(fwimgload); 157 STAT_INIT(nsformat); 158 STAT_INIT(vendor); 159 STAT_INIT(other); 160 161 #undef STAT_INIT 162 #undef STAT_INIT_ONE 163 164 kstat_install(nvme->n_admin_kstat); 165 return (true); 166 } 167 168 void 169 nvme_stat_cleanup(nvme_t *nvme) 170 { 171 nvme_stat_device_cleanup(nvme); 172 nvme_stat_admin_cleanup(nvme); 173 } 174 175 boolean_t 176 nvme_stat_init(nvme_t *nvme) 177 { 178 if (!nvme_stat_device_init(nvme) || !nvme_stat_admin_init(nvme)) { 179 nvme_stat_cleanup(nvme); 180 return (B_FALSE); 181 } 182 return (B_TRUE); 183 } 184 185 void 186 nvme_admin_stat_cmd(nvme_t *nvme, nvme_cmd_t *cmd) 187 { 188 hrtime_t t; 189 uint64_t cnt, avg; 190 kstat_named_t *data, *cntd, *avgd, *maxd; 191 192 switch (cmd->nc_sqe.sqe_opc) { 193 case NVME_OPC_DELETE_SQUEUE: 194 case NVME_OPC_CREATE_SQUEUE: 195 case NVME_OPC_DELETE_CQUEUE: 196 case NVME_OPC_CREATE_CQUEUE: 197 /* No statistics are kept for these opcodes */ 198 return; 199 case NVME_OPC_GET_LOG_PAGE: 200 data = nvme->n_admin_stat.nas_getlogpage; 201 break; 202 case NVME_OPC_IDENTIFY: 203 data = nvme->n_admin_stat.nas_identify; 204 break; 205 case NVME_OPC_ABORT: 206 data = nvme->n_admin_stat.nas_abort; 207 break; 208 case NVME_OPC_FW_ACTIVATE: 209 data = nvme->n_admin_stat.nas_fwactivate; 210 break; 211 case NVME_OPC_FW_IMAGE_LOAD: 212 data = nvme->n_admin_stat.nas_fwimgload; 213 break; 214 case NVME_OPC_NVM_FORMAT: 215 data = nvme->n_admin_stat.nas_nsformat; 216 break; 217 case NVME_OPC_VENDOR_LOW ... NVME_OPC_VENDOR_HIGH: 218 data = nvme->n_admin_stat.nas_vendor; 219 break; 220 default: 221 data = nvme->n_admin_stat.nas_other; 222 break; 223 } 224 225 t = gethrtime() - cmd->nc_submit_ts; 226 227 cntd = &data[NAS_CNT]; 228 avgd = &data[NAS_AVG]; 229 maxd = &data[NAS_MAX]; 230 231 mutex_enter(&nvme->n_admin_stat_mutex); 232 cnt = cntd->value.ui64; 233 avg = avgd->value.ui64; 234 235 /* 236 * Update the cumulative rolling average. 237 * Since `t` and `avg` are orders of magnitude greater than `cnt` it 238 * is sufficient to directly adjust the current average towards the 239 * new value. 240 */ 241 if (t > avg) 242 avg += (t - avg) / (cnt + 1); 243 else 244 avg -= (avg - t) / (cnt + 1); 245 cntd->value.ui64++; 246 avgd->value.ui64 = avg; 247 if (t > maxd->value.ui64) 248 maxd->value.ui64 = t; 249 mutex_exit(&nvme->n_admin_stat_mutex); 250 } 251