1*a1eda741SJohn Baldwin /*- 2*a1eda741SJohn Baldwin * SPDX-License-Identifier: BSD-2-Clause 3*a1eda741SJohn Baldwin * 4*a1eda741SJohn Baldwin * Copyright (c) 2024 Chelsio Communications, Inc. 5*a1eda741SJohn Baldwin * Written by: John Baldwin <jhb@FreeBSD.org> 6*a1eda741SJohn Baldwin */ 7*a1eda741SJohn Baldwin 8*a1eda741SJohn Baldwin #include <sys/types.h> 9*a1eda741SJohn Baldwin #include <sys/bus.h> 10*a1eda741SJohn Baldwin #include <sys/lock.h> 11*a1eda741SJohn Baldwin #include <sys/malloc.h> 12*a1eda741SJohn Baldwin #include <sys/mutex.h> 13*a1eda741SJohn Baldwin #include <sys/taskqueue.h> 14*a1eda741SJohn Baldwin #include <dev/nvmf/host/nvmf_var.h> 15*a1eda741SJohn Baldwin 16*a1eda741SJohn Baldwin struct nvmf_aer { 17*a1eda741SJohn Baldwin struct nvmf_softc *sc; 18*a1eda741SJohn Baldwin uint8_t log_page_id; 19*a1eda741SJohn Baldwin uint8_t info; 20*a1eda741SJohn Baldwin uint8_t type; 21*a1eda741SJohn Baldwin 22*a1eda741SJohn Baldwin u_int page_len; 23*a1eda741SJohn Baldwin void *page; 24*a1eda741SJohn Baldwin 25*a1eda741SJohn Baldwin int error; 26*a1eda741SJohn Baldwin uint16_t status; 27*a1eda741SJohn Baldwin int pending; 28*a1eda741SJohn Baldwin struct mtx *lock; 29*a1eda741SJohn Baldwin struct task complete_task; 30*a1eda741SJohn Baldwin struct task finish_page_task; 31*a1eda741SJohn Baldwin }; 32*a1eda741SJohn Baldwin 33*a1eda741SJohn Baldwin #define MAX_LOG_PAGE_SIZE 4096 34*a1eda741SJohn Baldwin 35*a1eda741SJohn Baldwin static void nvmf_complete_aer(void *arg, const struct nvme_completion *cqe); 36*a1eda741SJohn Baldwin 37*a1eda741SJohn Baldwin static void 38*a1eda741SJohn Baldwin nvmf_submit_aer(struct nvmf_softc *sc, struct nvmf_aer *aer) 39*a1eda741SJohn Baldwin { 40*a1eda741SJohn Baldwin struct nvmf_request *req; 41*a1eda741SJohn Baldwin struct nvme_command cmd; 42*a1eda741SJohn Baldwin 43*a1eda741SJohn Baldwin memset(&cmd, 0, sizeof(cmd)); 44*a1eda741SJohn Baldwin cmd.opc = NVME_OPC_ASYNC_EVENT_REQUEST; 45*a1eda741SJohn Baldwin 46*a1eda741SJohn Baldwin req = nvmf_allocate_request(sc->admin, &cmd, nvmf_complete_aer, aer, 47*a1eda741SJohn Baldwin M_WAITOK); 48*a1eda741SJohn Baldwin if (req == NULL) 49*a1eda741SJohn Baldwin return; 50*a1eda741SJohn Baldwin req->aer = true; 51*a1eda741SJohn Baldwin nvmf_submit_request(req); 52*a1eda741SJohn Baldwin } 53*a1eda741SJohn Baldwin 54*a1eda741SJohn Baldwin static void 55*a1eda741SJohn Baldwin nvmf_handle_changed_namespaces(struct nvmf_softc *sc, 56*a1eda741SJohn Baldwin struct nvme_ns_list *ns_list) 57*a1eda741SJohn Baldwin { 58*a1eda741SJohn Baldwin uint32_t nsid; 59*a1eda741SJohn Baldwin 60*a1eda741SJohn Baldwin /* 61*a1eda741SJohn Baldwin * If more than 1024 namespaces have changed, we should 62*a1eda741SJohn Baldwin * probably just rescan the entire set of namespaces. 63*a1eda741SJohn Baldwin */ 64*a1eda741SJohn Baldwin if (ns_list->ns[0] == 0xffffffff) { 65*a1eda741SJohn Baldwin device_printf(sc->dev, "too many changed namespaces\n"); 66*a1eda741SJohn Baldwin return; 67*a1eda741SJohn Baldwin } 68*a1eda741SJohn Baldwin 69*a1eda741SJohn Baldwin for (u_int i = 0; i < nitems(ns_list->ns); i++) { 70*a1eda741SJohn Baldwin if (ns_list->ns[i] == 0) 71*a1eda741SJohn Baldwin break; 72*a1eda741SJohn Baldwin 73*a1eda741SJohn Baldwin nsid = le32toh(ns_list->ns[i]); 74*a1eda741SJohn Baldwin nvmf_rescan_ns(sc, nsid); 75*a1eda741SJohn Baldwin } 76*a1eda741SJohn Baldwin } 77*a1eda741SJohn Baldwin 78*a1eda741SJohn Baldwin static void 79*a1eda741SJohn Baldwin nvmf_finish_aer_page(struct nvmf_softc *sc, struct nvmf_aer *aer) 80*a1eda741SJohn Baldwin { 81*a1eda741SJohn Baldwin /* If an error occurred fetching the page, just bail. */ 82*a1eda741SJohn Baldwin if (aer->error != 0 || aer->status != 0) 83*a1eda741SJohn Baldwin return; 84*a1eda741SJohn Baldwin 85*a1eda741SJohn Baldwin taskqueue_enqueue(taskqueue_thread, &aer->finish_page_task); 86*a1eda741SJohn Baldwin } 87*a1eda741SJohn Baldwin 88*a1eda741SJohn Baldwin static void 89*a1eda741SJohn Baldwin nvmf_finish_aer_page_task(void *arg, int pending) 90*a1eda741SJohn Baldwin { 91*a1eda741SJohn Baldwin struct nvmf_aer *aer = arg; 92*a1eda741SJohn Baldwin struct nvmf_softc *sc = aer->sc; 93*a1eda741SJohn Baldwin 94*a1eda741SJohn Baldwin switch (aer->log_page_id) { 95*a1eda741SJohn Baldwin case NVME_LOG_ERROR: 96*a1eda741SJohn Baldwin /* TODO: Should we log these? */ 97*a1eda741SJohn Baldwin break; 98*a1eda741SJohn Baldwin case NVME_LOG_CHANGED_NAMESPACE: 99*a1eda741SJohn Baldwin nvmf_handle_changed_namespaces(sc, aer->page); 100*a1eda741SJohn Baldwin break; 101*a1eda741SJohn Baldwin } 102*a1eda741SJohn Baldwin 103*a1eda741SJohn Baldwin /* Resubmit this AER command. */ 104*a1eda741SJohn Baldwin nvmf_submit_aer(sc, aer); 105*a1eda741SJohn Baldwin } 106*a1eda741SJohn Baldwin 107*a1eda741SJohn Baldwin static void 108*a1eda741SJohn Baldwin nvmf_io_complete_aer_page(void *arg, size_t xfered, int error) 109*a1eda741SJohn Baldwin { 110*a1eda741SJohn Baldwin struct nvmf_aer *aer = arg; 111*a1eda741SJohn Baldwin struct nvmf_softc *sc = aer->sc; 112*a1eda741SJohn Baldwin 113*a1eda741SJohn Baldwin mtx_lock(aer->lock); 114*a1eda741SJohn Baldwin aer->error = error; 115*a1eda741SJohn Baldwin aer->pending--; 116*a1eda741SJohn Baldwin if (aer->pending == 0) { 117*a1eda741SJohn Baldwin mtx_unlock(aer->lock); 118*a1eda741SJohn Baldwin nvmf_finish_aer_page(sc, aer); 119*a1eda741SJohn Baldwin } else 120*a1eda741SJohn Baldwin mtx_unlock(aer->lock); 121*a1eda741SJohn Baldwin } 122*a1eda741SJohn Baldwin 123*a1eda741SJohn Baldwin static void 124*a1eda741SJohn Baldwin nvmf_complete_aer_page(void *arg, const struct nvme_completion *cqe) 125*a1eda741SJohn Baldwin { 126*a1eda741SJohn Baldwin struct nvmf_aer *aer = arg; 127*a1eda741SJohn Baldwin struct nvmf_softc *sc = aer->sc; 128*a1eda741SJohn Baldwin 129*a1eda741SJohn Baldwin mtx_lock(aer->lock); 130*a1eda741SJohn Baldwin aer->status = cqe->status; 131*a1eda741SJohn Baldwin aer->pending--; 132*a1eda741SJohn Baldwin if (aer->pending == 0) { 133*a1eda741SJohn Baldwin mtx_unlock(aer->lock); 134*a1eda741SJohn Baldwin nvmf_finish_aer_page(sc, aer); 135*a1eda741SJohn Baldwin } else 136*a1eda741SJohn Baldwin mtx_unlock(aer->lock); 137*a1eda741SJohn Baldwin } 138*a1eda741SJohn Baldwin 139*a1eda741SJohn Baldwin static u_int 140*a1eda741SJohn Baldwin nvmf_log_page_size(struct nvmf_softc *sc, uint8_t log_page_id) 141*a1eda741SJohn Baldwin { 142*a1eda741SJohn Baldwin switch (log_page_id) { 143*a1eda741SJohn Baldwin case NVME_LOG_ERROR: 144*a1eda741SJohn Baldwin return ((sc->cdata->elpe + 1) * 145*a1eda741SJohn Baldwin sizeof(struct nvme_error_information_entry)); 146*a1eda741SJohn Baldwin case NVME_LOG_CHANGED_NAMESPACE: 147*a1eda741SJohn Baldwin return (sizeof(struct nvme_ns_list)); 148*a1eda741SJohn Baldwin default: 149*a1eda741SJohn Baldwin return (0); 150*a1eda741SJohn Baldwin } 151*a1eda741SJohn Baldwin } 152*a1eda741SJohn Baldwin 153*a1eda741SJohn Baldwin static void 154*a1eda741SJohn Baldwin nvmf_complete_aer(void *arg, const struct nvme_completion *cqe) 155*a1eda741SJohn Baldwin { 156*a1eda741SJohn Baldwin struct nvmf_aer *aer = arg; 157*a1eda741SJohn Baldwin struct nvmf_softc *sc = aer->sc; 158*a1eda741SJohn Baldwin uint32_t cdw0; 159*a1eda741SJohn Baldwin 160*a1eda741SJohn Baldwin /* 161*a1eda741SJohn Baldwin * The only error defined for AER is an abort due to 162*a1eda741SJohn Baldwin * submitting too many AER commands. Just discard this AER 163*a1eda741SJohn Baldwin * without resubmitting if we get an error. 164*a1eda741SJohn Baldwin * 165*a1eda741SJohn Baldwin * NB: Pending AER commands are aborted during controller 166*a1eda741SJohn Baldwin * shutdown, so discard aborted commands silently. 167*a1eda741SJohn Baldwin */ 168*a1eda741SJohn Baldwin if (cqe->status != 0) { 169*a1eda741SJohn Baldwin if (!nvmf_cqe_aborted(cqe)) 170*a1eda741SJohn Baldwin device_printf(sc->dev, "Ignoring error %#x for AER\n", 171*a1eda741SJohn Baldwin le16toh(cqe->status)); 172*a1eda741SJohn Baldwin return; 173*a1eda741SJohn Baldwin } 174*a1eda741SJohn Baldwin 175*a1eda741SJohn Baldwin cdw0 = le32toh(cqe->cdw0); 176*a1eda741SJohn Baldwin aer->log_page_id = NVMEV(NVME_ASYNC_EVENT_LOG_PAGE_ID, cdw0); 177*a1eda741SJohn Baldwin aer->info = NVMEV(NVME_ASYNC_EVENT_INFO, cdw0); 178*a1eda741SJohn Baldwin aer->type = NVMEV(NVME_ASYNC_EVENT_TYPE, cdw0); 179*a1eda741SJohn Baldwin 180*a1eda741SJohn Baldwin device_printf(sc->dev, "AER type %u, info %#x, page %#x\n", 181*a1eda741SJohn Baldwin aer->type, aer->info, aer->log_page_id); 182*a1eda741SJohn Baldwin 183*a1eda741SJohn Baldwin aer->page_len = nvmf_log_page_size(sc, aer->log_page_id); 184*a1eda741SJohn Baldwin taskqueue_enqueue(taskqueue_thread, &aer->complete_task); 185*a1eda741SJohn Baldwin } 186*a1eda741SJohn Baldwin 187*a1eda741SJohn Baldwin static void 188*a1eda741SJohn Baldwin nvmf_complete_aer_task(void *arg, int pending) 189*a1eda741SJohn Baldwin { 190*a1eda741SJohn Baldwin struct nvmf_aer *aer = arg; 191*a1eda741SJohn Baldwin struct nvmf_softc *sc = aer->sc; 192*a1eda741SJohn Baldwin 193*a1eda741SJohn Baldwin if (aer->page_len != 0) { 194*a1eda741SJohn Baldwin /* Read the associated log page. */ 195*a1eda741SJohn Baldwin aer->page_len = MIN(aer->page_len, MAX_LOG_PAGE_SIZE); 196*a1eda741SJohn Baldwin aer->pending = 2; 197*a1eda741SJohn Baldwin (void) nvmf_cmd_get_log_page(sc, NVME_GLOBAL_NAMESPACE_TAG, 198*a1eda741SJohn Baldwin aer->log_page_id, 0, aer->page, aer->page_len, 199*a1eda741SJohn Baldwin nvmf_complete_aer_page, aer, nvmf_io_complete_aer_page, 200*a1eda741SJohn Baldwin aer, M_WAITOK); 201*a1eda741SJohn Baldwin } else { 202*a1eda741SJohn Baldwin /* Resubmit this AER command. */ 203*a1eda741SJohn Baldwin nvmf_submit_aer(sc, aer); 204*a1eda741SJohn Baldwin } 205*a1eda741SJohn Baldwin } 206*a1eda741SJohn Baldwin 207*a1eda741SJohn Baldwin static int 208*a1eda741SJohn Baldwin nvmf_set_async_event_config(struct nvmf_softc *sc, uint32_t config) 209*a1eda741SJohn Baldwin { 210*a1eda741SJohn Baldwin struct nvme_command cmd; 211*a1eda741SJohn Baldwin struct nvmf_completion_status status; 212*a1eda741SJohn Baldwin struct nvmf_request *req; 213*a1eda741SJohn Baldwin 214*a1eda741SJohn Baldwin memset(&cmd, 0, sizeof(cmd)); 215*a1eda741SJohn Baldwin cmd.opc = NVME_OPC_SET_FEATURES; 216*a1eda741SJohn Baldwin cmd.cdw10 = htole32(NVME_FEAT_ASYNC_EVENT_CONFIGURATION); 217*a1eda741SJohn Baldwin cmd.cdw11 = htole32(config); 218*a1eda741SJohn Baldwin 219*a1eda741SJohn Baldwin nvmf_status_init(&status); 220*a1eda741SJohn Baldwin req = nvmf_allocate_request(sc->admin, &cmd, nvmf_complete, &status, 221*a1eda741SJohn Baldwin M_WAITOK); 222*a1eda741SJohn Baldwin if (req == NULL) { 223*a1eda741SJohn Baldwin device_printf(sc->dev, 224*a1eda741SJohn Baldwin "failed to allocate SET_FEATURES (ASYNC_EVENT_CONFIGURATION) command\n"); 225*a1eda741SJohn Baldwin return (ECONNABORTED); 226*a1eda741SJohn Baldwin } 227*a1eda741SJohn Baldwin nvmf_submit_request(req); 228*a1eda741SJohn Baldwin nvmf_wait_for_reply(&status); 229*a1eda741SJohn Baldwin 230*a1eda741SJohn Baldwin if (status.cqe.status != 0) { 231*a1eda741SJohn Baldwin device_printf(sc->dev, 232*a1eda741SJohn Baldwin "SET_FEATURES (ASYNC_EVENT_CONFIGURATION) failed, status %#x\n", 233*a1eda741SJohn Baldwin le16toh(status.cqe.status)); 234*a1eda741SJohn Baldwin return (EIO); 235*a1eda741SJohn Baldwin } 236*a1eda741SJohn Baldwin 237*a1eda741SJohn Baldwin return (0); 238*a1eda741SJohn Baldwin } 239*a1eda741SJohn Baldwin 240*a1eda741SJohn Baldwin void 241*a1eda741SJohn Baldwin nvmf_init_aer(struct nvmf_softc *sc) 242*a1eda741SJohn Baldwin { 243*a1eda741SJohn Baldwin /* 8 matches NVME_MAX_ASYNC_EVENTS */ 244*a1eda741SJohn Baldwin sc->num_aer = min(8, sc->cdata->aerl + 1); 245*a1eda741SJohn Baldwin sc->aer = mallocarray(sc->num_aer, sizeof(*sc->aer), M_NVMF, 246*a1eda741SJohn Baldwin M_WAITOK | M_ZERO); 247*a1eda741SJohn Baldwin for (u_int i = 0; i < sc->num_aer; i++) { 248*a1eda741SJohn Baldwin sc->aer[i].sc = sc; 249*a1eda741SJohn Baldwin sc->aer[i].page = malloc(MAX_LOG_PAGE_SIZE, M_NVMF, M_WAITOK); 250*a1eda741SJohn Baldwin sc->aer[i].lock = mtx_pool_find(mtxpool_sleep, &sc->aer[i]); 251*a1eda741SJohn Baldwin TASK_INIT(&sc->aer[i].complete_task, 0, nvmf_complete_aer_task, 252*a1eda741SJohn Baldwin &sc->aer[i]); 253*a1eda741SJohn Baldwin TASK_INIT(&sc->aer[i].finish_page_task, 0, 254*a1eda741SJohn Baldwin nvmf_finish_aer_page_task, &sc->aer[i]); 255*a1eda741SJohn Baldwin } 256*a1eda741SJohn Baldwin } 257*a1eda741SJohn Baldwin 258*a1eda741SJohn Baldwin int 259*a1eda741SJohn Baldwin nvmf_start_aer(struct nvmf_softc *sc) 260*a1eda741SJohn Baldwin { 261*a1eda741SJohn Baldwin uint32_t async_event_config; 262*a1eda741SJohn Baldwin int error; 263*a1eda741SJohn Baldwin 264*a1eda741SJohn Baldwin async_event_config = NVME_CRIT_WARN_ST_AVAILABLE_SPARE | 265*a1eda741SJohn Baldwin NVME_CRIT_WARN_ST_DEVICE_RELIABILITY | 266*a1eda741SJohn Baldwin NVME_CRIT_WARN_ST_READ_ONLY | 267*a1eda741SJohn Baldwin NVME_CRIT_WARN_ST_VOLATILE_MEMORY_BACKUP; 268*a1eda741SJohn Baldwin if (sc->cdata->ver >= NVME_REV(1, 2)) 269*a1eda741SJohn Baldwin async_event_config |= 270*a1eda741SJohn Baldwin sc->cdata->oaes & NVME_ASYNC_EVENT_NS_ATTRIBUTE; 271*a1eda741SJohn Baldwin error = nvmf_set_async_event_config(sc, async_event_config); 272*a1eda741SJohn Baldwin if (error != 0) 273*a1eda741SJohn Baldwin return (error); 274*a1eda741SJohn Baldwin 275*a1eda741SJohn Baldwin for (u_int i = 0; i < sc->num_aer; i++) 276*a1eda741SJohn Baldwin nvmf_submit_aer(sc, &sc->aer[i]); 277*a1eda741SJohn Baldwin 278*a1eda741SJohn Baldwin return (0); 279*a1eda741SJohn Baldwin } 280*a1eda741SJohn Baldwin 281*a1eda741SJohn Baldwin void 282*a1eda741SJohn Baldwin nvmf_destroy_aer(struct nvmf_softc *sc) 283*a1eda741SJohn Baldwin { 284*a1eda741SJohn Baldwin for (u_int i = 0; i < sc->num_aer; i++) { 285*a1eda741SJohn Baldwin taskqueue_drain(taskqueue_thread, &sc->aer[i].complete_task); 286*a1eda741SJohn Baldwin taskqueue_drain(taskqueue_thread, &sc->aer[i].finish_page_task); 287*a1eda741SJohn Baldwin free(sc->aer[i].page, M_NVMF); 288*a1eda741SJohn Baldwin } 289*a1eda741SJohn Baldwin free(sc->aer, M_NVMF); 290*a1eda741SJohn Baldwin } 291