xref: /freebsd/sys/dev/nvme/nvme_qpair.c (revision 21b6da584b35ff0ec85cb31c5e27affa573506dc)
1bb0ec6b3SJim Harris /*-
2bb0ec6b3SJim Harris  * Copyright (C) 2012 Intel Corporation
3bb0ec6b3SJim Harris  * All rights reserved.
4bb0ec6b3SJim Harris  *
5bb0ec6b3SJim Harris  * Redistribution and use in source and binary forms, with or without
6bb0ec6b3SJim Harris  * modification, are permitted provided that the following conditions
7bb0ec6b3SJim Harris  * are met:
8bb0ec6b3SJim Harris  * 1. Redistributions of source code must retain the above copyright
9bb0ec6b3SJim Harris  *    notice, this list of conditions and the following disclaimer.
10bb0ec6b3SJim Harris  * 2. Redistributions in binary form must reproduce the above copyright
11bb0ec6b3SJim Harris  *    notice, this list of conditions and the following disclaimer in the
12bb0ec6b3SJim Harris  *    documentation and/or other materials provided with the distribution.
13bb0ec6b3SJim Harris  *
14bb0ec6b3SJim Harris  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15bb0ec6b3SJim Harris  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16bb0ec6b3SJim Harris  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17bb0ec6b3SJim Harris  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18bb0ec6b3SJim Harris  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19bb0ec6b3SJim Harris  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20bb0ec6b3SJim Harris  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21bb0ec6b3SJim Harris  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22bb0ec6b3SJim Harris  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23bb0ec6b3SJim Harris  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24bb0ec6b3SJim Harris  * SUCH DAMAGE.
25bb0ec6b3SJim Harris  */
26bb0ec6b3SJim Harris 
27bb0ec6b3SJim Harris #include <sys/cdefs.h>
28bb0ec6b3SJim Harris __FBSDID("$FreeBSD$");
29bb0ec6b3SJim Harris 
30bb0ec6b3SJim Harris #include <sys/param.h>
31bb0ec6b3SJim Harris #include <sys/bus.h>
32bb0ec6b3SJim Harris 
33bb0ec6b3SJim Harris #include "nvme_private.h"
34bb0ec6b3SJim Harris 
35bb0ec6b3SJim Harris static boolean_t
36bb0ec6b3SJim Harris nvme_completion_check_retry(const struct nvme_completion *cpl)
37bb0ec6b3SJim Harris {
38bb0ec6b3SJim Harris 	/*
39bb0ec6b3SJim Harris 	 * TODO: spec is not clear how commands that are aborted due
40bb0ec6b3SJim Harris 	 *  to TLER will be marked.  So for now, it seems
41bb0ec6b3SJim Harris 	 *  NAMESPACE_NOT_READY is the only case where we should
42bb0ec6b3SJim Harris 	 *  look at the DNR bit.
43bb0ec6b3SJim Harris 	 */
44bb0ec6b3SJim Harris 	switch (cpl->sf_sct) {
45bb0ec6b3SJim Harris 	case NVME_SCT_GENERIC:
46bb0ec6b3SJim Harris 		switch (cpl->sf_sc) {
47bb0ec6b3SJim Harris 		case NVME_SC_NAMESPACE_NOT_READY:
48bb0ec6b3SJim Harris 			if (cpl->sf_dnr)
49bb0ec6b3SJim Harris 				return (0);
50bb0ec6b3SJim Harris 			else
51bb0ec6b3SJim Harris 				return (1);
52bb0ec6b3SJim Harris 		case NVME_SC_INVALID_OPCODE:
53bb0ec6b3SJim Harris 		case NVME_SC_INVALID_FIELD:
54bb0ec6b3SJim Harris 		case NVME_SC_COMMAND_ID_CONFLICT:
55bb0ec6b3SJim Harris 		case NVME_SC_DATA_TRANSFER_ERROR:
56bb0ec6b3SJim Harris 		case NVME_SC_ABORTED_POWER_LOSS:
57bb0ec6b3SJim Harris 		case NVME_SC_INTERNAL_DEVICE_ERROR:
58bb0ec6b3SJim Harris 		case NVME_SC_ABORTED_BY_REQUEST:
59bb0ec6b3SJim Harris 		case NVME_SC_ABORTED_SQ_DELETION:
60bb0ec6b3SJim Harris 		case NVME_SC_ABORTED_FAILED_FUSED:
61bb0ec6b3SJim Harris 		case NVME_SC_ABORTED_MISSING_FUSED:
62bb0ec6b3SJim Harris 		case NVME_SC_INVALID_NAMESPACE_OR_FORMAT:
63bb0ec6b3SJim Harris 		case NVME_SC_COMMAND_SEQUENCE_ERROR:
64bb0ec6b3SJim Harris 		case NVME_SC_LBA_OUT_OF_RANGE:
65bb0ec6b3SJim Harris 		case NVME_SC_CAPACITY_EXCEEDED:
66bb0ec6b3SJim Harris 		default:
67bb0ec6b3SJim Harris 			return (0);
68bb0ec6b3SJim Harris 		}
69bb0ec6b3SJim Harris 	case NVME_SCT_COMMAND_SPECIFIC:
70bb0ec6b3SJim Harris 	case NVME_SCT_MEDIA_ERROR:
71bb0ec6b3SJim Harris 	case NVME_SCT_VENDOR_SPECIFIC:
72bb0ec6b3SJim Harris 	default:
73bb0ec6b3SJim Harris 		return (0);
74bb0ec6b3SJim Harris 	}
75bb0ec6b3SJim Harris }
76bb0ec6b3SJim Harris 
77*21b6da58SJim Harris static void
78*21b6da58SJim Harris nvme_qpair_construct_tracker(struct nvme_qpair *qpair, struct nvme_tracker *tr,
79*21b6da58SJim Harris     uint16_t cid)
80bb0ec6b3SJim Harris {
819eb93f29SJim Harris 
82f2b19f67SJim Harris 	bus_dmamap_create(qpair->dma_tag, 0, &tr->payload_dma_map);
83f2b19f67SJim Harris 	bus_dmamap_create(qpair->dma_tag, 0, &tr->prp_dma_map);
84f2b19f67SJim Harris 
85f2b19f67SJim Harris 	bus_dmamap_load(qpair->dma_tag, tr->prp_dma_map, tr->prp,
86f2b19f67SJim Harris 	    sizeof(tr->prp), nvme_single_map, &tr->prp_bus_addr, 0);
87f2b19f67SJim Harris 
88bb0ec6b3SJim Harris 	callout_init_mtx(&tr->timer, &qpair->lock, 0);
89*21b6da58SJim Harris 	tr->cid = cid;
90d281e8fbSJim Harris 	tr->qpair = qpair;
91bb0ec6b3SJim Harris }
92bb0ec6b3SJim Harris 
93bb0ec6b3SJim Harris void
94bb0ec6b3SJim Harris nvme_qpair_process_completions(struct nvme_qpair *qpair)
95bb0ec6b3SJim Harris {
96bb0ec6b3SJim Harris 	struct nvme_tracker	*tr;
97ad697276SJim Harris 	struct nvme_request	*req;
98bb0ec6b3SJim Harris 	struct nvme_completion	*cpl;
99bb0ec6b3SJim Harris 	boolean_t		retry, error;
100bb0ec6b3SJim Harris 
1016568ebfcSJim Harris 	qpair->num_intr_handler_calls++;
1026568ebfcSJim Harris 
103bb0ec6b3SJim Harris 	while (1) {
104bb0ec6b3SJim Harris 		cpl = &qpair->cpl[qpair->cq_head];
105bb0ec6b3SJim Harris 
106bb0ec6b3SJim Harris 		if (cpl->p != qpair->phase)
107bb0ec6b3SJim Harris 			break;
108bb0ec6b3SJim Harris 
109bb0ec6b3SJim Harris 		tr = qpair->act_tr[cpl->cid];
110ad697276SJim Harris 		req = tr->req;
111ad697276SJim Harris 
112bb0ec6b3SJim Harris 		KASSERT(tr,
113bb0ec6b3SJim Harris 		    ("completion queue has entries but no active trackers\n"));
114bb0ec6b3SJim Harris 
115bb0ec6b3SJim Harris 		error = cpl->sf_sc || cpl->sf_sct;
116bb0ec6b3SJim Harris 		retry = error && nvme_completion_check_retry(cpl);
117bb0ec6b3SJim Harris 
118bb0ec6b3SJim Harris 		if (error) {
119bb0ec6b3SJim Harris 			nvme_dump_completion(cpl);
120ad697276SJim Harris 			nvme_dump_command(&tr->req->cmd);
121bb0ec6b3SJim Harris 		}
122bb0ec6b3SJim Harris 
123bb0ec6b3SJim Harris 		qpair->act_tr[cpl->cid] = NULL;
124bb0ec6b3SJim Harris 
125bb0ec6b3SJim Harris 		KASSERT(cpl->cid == tr->cmd.cid,
126bb0ec6b3SJim Harris 		    ("cpl cid does not match cmd cid\n"));
127bb0ec6b3SJim Harris 
128ad697276SJim Harris 		if (req->cb_fn && !retry)
129ad697276SJim Harris 			req->cb_fn(req->cb_arg, cpl);
130bb0ec6b3SJim Harris 
131bb0ec6b3SJim Harris 		qpair->sq_head = cpl->sqhd;
132bb0ec6b3SJim Harris 
133bb0ec6b3SJim Harris 		mtx_lock(&qpair->lock);
134bb0ec6b3SJim Harris 		callout_stop(&tr->timer);
135bb0ec6b3SJim Harris 
136bb0ec6b3SJim Harris 		if (retry)
137bb0ec6b3SJim Harris 			nvme_qpair_submit_cmd(qpair, tr);
138bb0ec6b3SJim Harris 		else {
1395fa5cc5fSJim Harris 			if (req->payload_size > 0 || req->uio != NULL)
140f2b19f67SJim Harris 				bus_dmamap_unload(qpair->dma_tag,
141f2b19f67SJim Harris 				    tr->payload_dma_map);
142bb0ec6b3SJim Harris 
143ad697276SJim Harris 			nvme_free_request(req);
144*21b6da58SJim Harris 
145*21b6da58SJim Harris 			if (SLIST_EMPTY(&qpair->free_tr))
146*21b6da58SJim Harris 				wakeup(qpair);
147*21b6da58SJim Harris 
148bb0ec6b3SJim Harris 			SLIST_INSERT_HEAD(&qpair->free_tr, tr, slist);
149c2e83b40SJim Harris 		}
150bb0ec6b3SJim Harris 
151bb0ec6b3SJim Harris 		mtx_unlock(&qpair->lock);
152bb0ec6b3SJim Harris 
153bb0ec6b3SJim Harris 		if (++qpair->cq_head == qpair->num_entries) {
154bb0ec6b3SJim Harris 			qpair->cq_head = 0;
155bb0ec6b3SJim Harris 			qpair->phase = !qpair->phase;
156bb0ec6b3SJim Harris 		}
157bb0ec6b3SJim Harris 
158bb0ec6b3SJim Harris 		nvme_mmio_write_4(qpair->ctrlr, doorbell[qpair->id].cq_hdbl,
159bb0ec6b3SJim Harris 		    qpair->cq_head);
160bb0ec6b3SJim Harris 	}
161bb0ec6b3SJim Harris }
162bb0ec6b3SJim Harris 
163bb0ec6b3SJim Harris static void
164bb0ec6b3SJim Harris nvme_qpair_msix_handler(void *arg)
165bb0ec6b3SJim Harris {
166bb0ec6b3SJim Harris 	struct nvme_qpair *qpair = arg;
167bb0ec6b3SJim Harris 
168bb0ec6b3SJim Harris 	nvme_qpair_process_completions(qpair);
169bb0ec6b3SJim Harris }
170bb0ec6b3SJim Harris 
171bb0ec6b3SJim Harris void
172bb0ec6b3SJim Harris nvme_qpair_construct(struct nvme_qpair *qpair, uint32_t id,
173*21b6da58SJim Harris     uint16_t vector, uint32_t num_entries, uint32_t num_trackers,
174*21b6da58SJim Harris     uint32_t max_xfer_size, struct nvme_controller *ctrlr)
175bb0ec6b3SJim Harris {
176*21b6da58SJim Harris 	struct nvme_tracker	*tr;
177*21b6da58SJim Harris 	uint32_t		i;
178bb0ec6b3SJim Harris 
179bb0ec6b3SJim Harris 	qpair->id = id;
180bb0ec6b3SJim Harris 	qpair->vector = vector;
181bb0ec6b3SJim Harris 	qpair->num_entries = num_entries;
182bb0ec6b3SJim Harris 	qpair->max_xfer_size = max_xfer_size;
183bb0ec6b3SJim Harris 	qpair->ctrlr = ctrlr;
184bb0ec6b3SJim Harris 
185bb0ec6b3SJim Harris 	/*
186bb0ec6b3SJim Harris 	 * First time through the completion queue, HW will set phase
187bb0ec6b3SJim Harris 	 *  bit on completions to 1.  So set this to 1 here, indicating
188bb0ec6b3SJim Harris 	 *  we're looking for a 1 to know which entries have completed.
189bb0ec6b3SJim Harris 	 *  we'll toggle the bit each time when the completion queue
190bb0ec6b3SJim Harris 	 *  rolls over.
191bb0ec6b3SJim Harris 	 */
192bb0ec6b3SJim Harris 	qpair->phase = 1;
193bb0ec6b3SJim Harris 
194bb0ec6b3SJim Harris 	if (ctrlr->msix_enabled) {
195bb0ec6b3SJim Harris 
196bb0ec6b3SJim Harris 		/*
197bb0ec6b3SJim Harris 		 * MSI-X vector resource IDs start at 1, so we add one to
198bb0ec6b3SJim Harris 		 *  the queue's vector to get the corresponding rid to use.
199bb0ec6b3SJim Harris 		 */
200bb0ec6b3SJim Harris 		qpair->rid = vector + 1;
201bb0ec6b3SJim Harris 
202bb0ec6b3SJim Harris 		qpair->res = bus_alloc_resource_any(ctrlr->dev, SYS_RES_IRQ,
203bb0ec6b3SJim Harris 		    &qpair->rid, RF_ACTIVE);
204bb0ec6b3SJim Harris 
205bb0ec6b3SJim Harris 		bus_setup_intr(ctrlr->dev, qpair->res,
206bb0ec6b3SJim Harris 		    INTR_TYPE_MISC | INTR_MPSAFE, NULL,
207bb0ec6b3SJim Harris 		    nvme_qpair_msix_handler, qpair, &qpair->tag);
208bb0ec6b3SJim Harris 	}
209bb0ec6b3SJim Harris 
210bb0ec6b3SJim Harris 	mtx_init(&qpair->lock, "nvme qpair lock", NULL, MTX_DEF);
211bb0ec6b3SJim Harris 
212bb0ec6b3SJim Harris 	bus_dma_tag_create(bus_get_dma_tag(ctrlr->dev),
213bb0ec6b3SJim Harris 	    sizeof(uint64_t), PAGE_SIZE, BUS_SPACE_MAXADDR,
214bb0ec6b3SJim Harris 	    BUS_SPACE_MAXADDR, NULL, NULL, qpair->max_xfer_size,
215bb0ec6b3SJim Harris 	    (qpair->max_xfer_size/PAGE_SIZE)+1, PAGE_SIZE, 0,
216bb0ec6b3SJim Harris 	    NULL, NULL, &qpair->dma_tag);
217bb0ec6b3SJim Harris 
218bb0ec6b3SJim Harris 	qpair->num_cmds = 0;
2196568ebfcSJim Harris 	qpair->num_intr_handler_calls = 0;
220*21b6da58SJim Harris 	qpair->num_trackers = num_trackers;
221bb0ec6b3SJim Harris 	qpair->sq_head = qpair->sq_tail = qpair->cq_head = 0;
222bb0ec6b3SJim Harris 
223bb0ec6b3SJim Harris 	/* TODO: error checking on contigmalloc, bus_dmamap_load calls */
224bb0ec6b3SJim Harris 	qpair->cmd = contigmalloc(qpair->num_entries *
225bb0ec6b3SJim Harris 	    sizeof(struct nvme_command), M_NVME, M_ZERO | M_NOWAIT,
226bb0ec6b3SJim Harris 	    0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0);
227bb0ec6b3SJim Harris 	qpair->cpl = contigmalloc(qpair->num_entries *
228bb0ec6b3SJim Harris 	    sizeof(struct nvme_completion), M_NVME, M_ZERO | M_NOWAIT,
229bb0ec6b3SJim Harris 	    0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0);
230bb0ec6b3SJim Harris 
231bb0ec6b3SJim Harris 	bus_dmamap_create(qpair->dma_tag, 0, &qpair->cmd_dma_map);
232bb0ec6b3SJim Harris 	bus_dmamap_create(qpair->dma_tag, 0, &qpair->cpl_dma_map);
233bb0ec6b3SJim Harris 
234bb0ec6b3SJim Harris 	bus_dmamap_load(qpair->dma_tag, qpair->cmd_dma_map,
235bb0ec6b3SJim Harris 	    qpair->cmd, qpair->num_entries * sizeof(struct nvme_command),
236bb0ec6b3SJim Harris 	    nvme_single_map, &qpair->cmd_bus_addr, 0);
237bb0ec6b3SJim Harris 	bus_dmamap_load(qpair->dma_tag, qpair->cpl_dma_map,
238bb0ec6b3SJim Harris 	    qpair->cpl, qpair->num_entries * sizeof(struct nvme_completion),
239bb0ec6b3SJim Harris 	    nvme_single_map, &qpair->cpl_bus_addr, 0);
240bb0ec6b3SJim Harris 
241bb0ec6b3SJim Harris 	qpair->sq_tdbl_off = nvme_mmio_offsetof(doorbell[id].sq_tdbl);
242bb0ec6b3SJim Harris 	qpair->cq_hdbl_off = nvme_mmio_offsetof(doorbell[id].cq_hdbl);
243bb0ec6b3SJim Harris 
244bb0ec6b3SJim Harris 	SLIST_INIT(&qpair->free_tr);
245bb0ec6b3SJim Harris 
246*21b6da58SJim Harris 	for (i = 0; i < num_trackers; i++) {
247*21b6da58SJim Harris 		tr = malloc(sizeof(*tr), M_NVME, M_ZERO | M_NOWAIT);
248*21b6da58SJim Harris 
249*21b6da58SJim Harris 		if (tr == NULL) {
250*21b6da58SJim Harris 			printf("warning: nvme tracker malloc failed\n");
251*21b6da58SJim Harris 			break;
252*21b6da58SJim Harris 		}
253*21b6da58SJim Harris 
254*21b6da58SJim Harris 		nvme_qpair_construct_tracker(qpair, tr, i);
255*21b6da58SJim Harris 		SLIST_INSERT_HEAD(&qpair->free_tr, tr, slist);
256*21b6da58SJim Harris 	}
257*21b6da58SJim Harris 
258bb0ec6b3SJim Harris 	qpair->act_tr = malloc(sizeof(struct nvme_tracker *) * qpair->num_entries,
259bb0ec6b3SJim Harris 	    M_NVME, M_ZERO | M_NOWAIT);
260bb0ec6b3SJim Harris }
261bb0ec6b3SJim Harris 
262bb0ec6b3SJim Harris static void
263bb0ec6b3SJim Harris nvme_qpair_destroy(struct nvme_qpair *qpair)
264bb0ec6b3SJim Harris {
265bb0ec6b3SJim Harris 	struct nvme_tracker *tr;
266bb0ec6b3SJim Harris 
267bb0ec6b3SJim Harris 	if (qpair->tag)
268bb0ec6b3SJim Harris 		bus_teardown_intr(qpair->ctrlr->dev, qpair->res, qpair->tag);
269bb0ec6b3SJim Harris 
270bb0ec6b3SJim Harris 	if (qpair->res)
271bb0ec6b3SJim Harris 		bus_release_resource(qpair->ctrlr->dev, SYS_RES_IRQ,
272bb0ec6b3SJim Harris 		    rman_get_rid(qpair->res), qpair->res);
273bb0ec6b3SJim Harris 
274bb0ec6b3SJim Harris 	if (qpair->dma_tag)
275bb0ec6b3SJim Harris 		bus_dma_tag_destroy(qpair->dma_tag);
276bb0ec6b3SJim Harris 
277bb0ec6b3SJim Harris 	if (qpair->act_tr)
278bb0ec6b3SJim Harris 		free(qpair->act_tr, M_NVME);
279bb0ec6b3SJim Harris 
280bb0ec6b3SJim Harris 	while (!SLIST_EMPTY(&qpair->free_tr)) {
281bb0ec6b3SJim Harris 		tr = SLIST_FIRST(&qpair->free_tr);
282bb0ec6b3SJim Harris 		SLIST_REMOVE_HEAD(&qpair->free_tr, slist);
283f2b19f67SJim Harris 		bus_dmamap_destroy(qpair->dma_tag, tr->payload_dma_map);
284f2b19f67SJim Harris 		bus_dmamap_destroy(qpair->dma_tag, tr->prp_dma_map);
285bb0ec6b3SJim Harris 		free(tr, M_NVME);
286bb0ec6b3SJim Harris 	}
287bb0ec6b3SJim Harris }
288bb0ec6b3SJim Harris 
289bb0ec6b3SJim Harris void
290bb0ec6b3SJim Harris nvme_admin_qpair_destroy(struct nvme_qpair *qpair)
291bb0ec6b3SJim Harris {
292bb0ec6b3SJim Harris 
293bb0ec6b3SJim Harris 	/*
294bb0ec6b3SJim Harris 	 * For NVMe, you don't send delete queue commands for the admin
295bb0ec6b3SJim Harris 	 *  queue, so we just need to unload and free the cmd and cpl memory.
296bb0ec6b3SJim Harris 	 */
297bb0ec6b3SJim Harris 	bus_dmamap_unload(qpair->dma_tag, qpair->cmd_dma_map);
298bb0ec6b3SJim Harris 	bus_dmamap_destroy(qpair->dma_tag, qpair->cmd_dma_map);
299bb0ec6b3SJim Harris 
300bb0ec6b3SJim Harris 	contigfree(qpair->cmd,
301bb0ec6b3SJim Harris 	    qpair->num_entries * sizeof(struct nvme_command), M_NVME);
302bb0ec6b3SJim Harris 
303bb0ec6b3SJim Harris 	bus_dmamap_unload(qpair->dma_tag, qpair->cpl_dma_map);
304bb0ec6b3SJim Harris 	bus_dmamap_destroy(qpair->dma_tag, qpair->cpl_dma_map);
305bb0ec6b3SJim Harris 	contigfree(qpair->cpl,
306bb0ec6b3SJim Harris 	    qpair->num_entries * sizeof(struct nvme_completion), M_NVME);
307bb0ec6b3SJim Harris 
308bb0ec6b3SJim Harris 	nvme_qpair_destroy(qpair);
309bb0ec6b3SJim Harris }
310bb0ec6b3SJim Harris 
311bb0ec6b3SJim Harris static void
312bb0ec6b3SJim Harris nvme_free_cmd_ring(void *arg, const struct nvme_completion *status)
313bb0ec6b3SJim Harris {
314bb0ec6b3SJim Harris 	struct nvme_qpair *qpair;
315bb0ec6b3SJim Harris 
316bb0ec6b3SJim Harris 	qpair = (struct nvme_qpair *)arg;
317bb0ec6b3SJim Harris 	bus_dmamap_unload(qpair->dma_tag, qpair->cmd_dma_map);
318bb0ec6b3SJim Harris 	bus_dmamap_destroy(qpair->dma_tag, qpair->cmd_dma_map);
319bb0ec6b3SJim Harris 	contigfree(qpair->cmd,
320bb0ec6b3SJim Harris 	    qpair->num_entries * sizeof(struct nvme_command), M_NVME);
321bb0ec6b3SJim Harris 	qpair->cmd = NULL;
322bb0ec6b3SJim Harris }
323bb0ec6b3SJim Harris 
324bb0ec6b3SJim Harris static void
325bb0ec6b3SJim Harris nvme_free_cpl_ring(void *arg, const struct nvme_completion *status)
326bb0ec6b3SJim Harris {
327bb0ec6b3SJim Harris 	struct nvme_qpair *qpair;
328bb0ec6b3SJim Harris 
329bb0ec6b3SJim Harris 	qpair = (struct nvme_qpair *)arg;
330bb0ec6b3SJim Harris 	bus_dmamap_unload(qpair->dma_tag, qpair->cpl_dma_map);
331bb0ec6b3SJim Harris 	bus_dmamap_destroy(qpair->dma_tag, qpair->cpl_dma_map);
332bb0ec6b3SJim Harris 	contigfree(qpair->cpl,
333bb0ec6b3SJim Harris 	    qpair->num_entries * sizeof(struct nvme_completion), M_NVME);
334bb0ec6b3SJim Harris 	qpair->cpl = NULL;
335bb0ec6b3SJim Harris }
336bb0ec6b3SJim Harris 
337bb0ec6b3SJim Harris void
338bb0ec6b3SJim Harris nvme_io_qpair_destroy(struct nvme_qpair *qpair)
339bb0ec6b3SJim Harris {
340bb0ec6b3SJim Harris 	struct nvme_controller *ctrlr = qpair->ctrlr;
341bb0ec6b3SJim Harris 
342bb0ec6b3SJim Harris 	if (qpair->num_entries > 0) {
343bb0ec6b3SJim Harris 
344bb0ec6b3SJim Harris 		nvme_ctrlr_cmd_delete_io_sq(ctrlr, qpair, nvme_free_cmd_ring,
345bb0ec6b3SJim Harris 		    qpair);
346bb0ec6b3SJim Harris 		/* Spin until free_cmd_ring sets qpair->cmd to NULL. */
347bb0ec6b3SJim Harris 		while (qpair->cmd)
348bb0ec6b3SJim Harris 			DELAY(5);
349bb0ec6b3SJim Harris 
350bb0ec6b3SJim Harris 		nvme_ctrlr_cmd_delete_io_cq(ctrlr, qpair, nvme_free_cpl_ring,
351bb0ec6b3SJim Harris 		    qpair);
352bb0ec6b3SJim Harris 		/* Spin until free_cpl_ring sets qpair->cmd to NULL. */
353bb0ec6b3SJim Harris 		while (qpair->cpl)
354bb0ec6b3SJim Harris 			DELAY(5);
355bb0ec6b3SJim Harris 
356bb0ec6b3SJim Harris 		nvme_qpair_destroy(qpair);
357bb0ec6b3SJim Harris 	}
358bb0ec6b3SJim Harris }
359bb0ec6b3SJim Harris 
360bb0ec6b3SJim Harris static void
361bb0ec6b3SJim Harris nvme_timeout(void *arg)
362bb0ec6b3SJim Harris {
363bb0ec6b3SJim Harris 	/*
364bb0ec6b3SJim Harris 	 * TODO: Add explicit abort operation here, once nvme(4) supports
365bb0ec6b3SJim Harris 	 *  abort commands.
366bb0ec6b3SJim Harris 	 */
367bb0ec6b3SJim Harris }
368bb0ec6b3SJim Harris 
369bb0ec6b3SJim Harris void
370bb0ec6b3SJim Harris nvme_qpair_submit_cmd(struct nvme_qpair *qpair, struct nvme_tracker *tr)
371bb0ec6b3SJim Harris {
372ad697276SJim Harris 	struct nvme_request *req;
373bb0ec6b3SJim Harris 
374ad697276SJim Harris 	req = tr->req;
375ad697276SJim Harris 	req->cmd.cid = tr->cid;
376bb0ec6b3SJim Harris 	qpair->act_tr[tr->cid] = tr;
377bb0ec6b3SJim Harris 
378bb0ec6b3SJim Harris 	callout_reset(&tr->timer, NVME_TIMEOUT_IN_SEC * hz, nvme_timeout, tr);
379bb0ec6b3SJim Harris 
380bb0ec6b3SJim Harris 	/* Copy the command from the tracker to the submission queue. */
381ad697276SJim Harris 	memcpy(&qpair->cmd[qpair->sq_tail], &req->cmd, sizeof(req->cmd));
382bb0ec6b3SJim Harris 
383bb0ec6b3SJim Harris 	if (++qpair->sq_tail == qpair->num_entries)
384bb0ec6b3SJim Harris 		qpair->sq_tail = 0;
385bb0ec6b3SJim Harris 
386bb0ec6b3SJim Harris 	wmb();
387bb0ec6b3SJim Harris 	nvme_mmio_write_4(qpair->ctrlr, doorbell[qpair->id].sq_tdbl,
388bb0ec6b3SJim Harris 	    qpair->sq_tail);
389bb0ec6b3SJim Harris 
390bb0ec6b3SJim Harris 	qpair->num_cmds++;
391bb0ec6b3SJim Harris }
3925ae9ed68SJim Harris 
3935ae9ed68SJim Harris void
3945ae9ed68SJim Harris nvme_qpair_submit_request(struct nvme_qpair *qpair, struct nvme_request *req)
3955ae9ed68SJim Harris {
3965ae9ed68SJim Harris 	struct nvme_tracker	*tr;
3975ae9ed68SJim Harris 	int			err;
3985ae9ed68SJim Harris 
3995ae9ed68SJim Harris 	mtx_lock(&qpair->lock);
4005ae9ed68SJim Harris 
401*21b6da58SJim Harris 	tr = SLIST_FIRST(&qpair->free_tr);
402*21b6da58SJim Harris 
403*21b6da58SJim Harris 	while (tr == NULL) {
404*21b6da58SJim Harris 		msleep(qpair, &qpair->lock, PRIBIO, "qpair_tr", 0);
405*21b6da58SJim Harris 		printf("msleep\n");
406*21b6da58SJim Harris 		tr = SLIST_FIRST(&qpair->free_tr);
407*21b6da58SJim Harris 	}
408*21b6da58SJim Harris 
409*21b6da58SJim Harris 	SLIST_REMOVE_HEAD(&qpair->free_tr, slist);
4105ae9ed68SJim Harris 	tr->req = req;
4115ae9ed68SJim Harris 
4125ae9ed68SJim Harris 	if (req->uio == NULL) {
4135ae9ed68SJim Harris 		if (req->payload_size > 0) {
4145ae9ed68SJim Harris 			err = bus_dmamap_load(tr->qpair->dma_tag,
4155ae9ed68SJim Harris 					      tr->payload_dma_map, req->payload,
4165ae9ed68SJim Harris 					      req->payload_size,
4175ae9ed68SJim Harris 					      nvme_payload_map, tr, 0);
4185ae9ed68SJim Harris 			if (err != 0)
4195ae9ed68SJim Harris 				panic("bus_dmamap_load returned non-zero!\n");
4205ae9ed68SJim Harris 		} else
4215ae9ed68SJim Harris 			nvme_qpair_submit_cmd(tr->qpair, tr);
4225ae9ed68SJim Harris 	} else {
4235ae9ed68SJim Harris 		err = bus_dmamap_load_uio(tr->qpair->dma_tag,
4245ae9ed68SJim Harris 					  tr->payload_dma_map, req->uio,
4255ae9ed68SJim Harris 					  nvme_payload_map_uio, tr, 0);
4265ae9ed68SJim Harris 		if (err != 0)
4275ae9ed68SJim Harris 			panic("bus_dmamap_load returned non-zero!\n");
4285ae9ed68SJim Harris 	}
4295ae9ed68SJim Harris 
4305ae9ed68SJim Harris 	mtx_unlock(&qpair->lock);
4315ae9ed68SJim Harris }
432