xref: /freebsd/sys/dev/nvmf/host/nvmf_aer.c (revision a1eda74167b5edb99fd31d507d8a3f7d7e14ae2b)
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