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