xref: /freebsd/sys/dev/nvme/nvme.c (revision 204498d7c27ef5e56e94d58204c528b106543777)
1bb0ec6b3SJim Harris /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
4496a2752SJim Harris  * Copyright (C) 2012-2014 Intel Corporation
5bb0ec6b3SJim Harris  * All rights reserved.
6bb0ec6b3SJim Harris  *
7bb0ec6b3SJim Harris  * Redistribution and use in source and binary forms, with or without
8bb0ec6b3SJim Harris  * modification, are permitted provided that the following conditions
9bb0ec6b3SJim Harris  * are met:
10bb0ec6b3SJim Harris  * 1. Redistributions of source code must retain the above copyright
11bb0ec6b3SJim Harris  *    notice, this list of conditions and the following disclaimer.
12bb0ec6b3SJim Harris  * 2. Redistributions in binary form must reproduce the above copyright
13bb0ec6b3SJim Harris  *    notice, this list of conditions and the following disclaimer in the
14bb0ec6b3SJim Harris  *    documentation and/or other materials provided with the distribution.
15bb0ec6b3SJim Harris  *
16bb0ec6b3SJim Harris  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17bb0ec6b3SJim Harris  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18bb0ec6b3SJim Harris  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19bb0ec6b3SJim Harris  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20bb0ec6b3SJim Harris  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21bb0ec6b3SJim Harris  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22bb0ec6b3SJim Harris  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23bb0ec6b3SJim Harris  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24bb0ec6b3SJim Harris  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25bb0ec6b3SJim Harris  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26bb0ec6b3SJim Harris  * SUCH DAMAGE.
27bb0ec6b3SJim Harris  */
28bb0ec6b3SJim Harris 
29bb0ec6b3SJim Harris #include <sys/cdefs.h>
30bb0ec6b3SJim Harris __FBSDID("$FreeBSD$");
31bb0ec6b3SJim Harris 
32bb0ec6b3SJim Harris #include <sys/param.h>
33bb0ec6b3SJim Harris #include <sys/bus.h>
34bb0ec6b3SJim Harris #include <sys/conf.h>
35bb0ec6b3SJim Harris #include <sys/module.h>
36bb0ec6b3SJim Harris 
37ad697276SJim Harris #include <vm/uma.h>
38ad697276SJim Harris 
39d891b199SJim Harris #include <dev/pci/pcireg.h>
40bb0ec6b3SJim Harris #include <dev/pci/pcivar.h>
41bb0ec6b3SJim Harris 
42bb0ec6b3SJim Harris #include "nvme_private.h"
43bb0ec6b3SJim Harris 
44bb0ec6b3SJim Harris struct nvme_consumer {
45038a5ee4SJim Harris 	uint32_t		id;
46038a5ee4SJim Harris 	nvme_cons_ns_fn_t	ns_fn;
47038a5ee4SJim Harris 	nvme_cons_ctrlr_fn_t	ctrlr_fn;
48038a5ee4SJim Harris 	nvme_cons_async_fn_t	async_fn;
49232e2edbSJim Harris 	nvme_cons_fail_fn_t	fail_fn;
50bb0ec6b3SJim Harris };
51bb0ec6b3SJim Harris 
52bb0ec6b3SJim Harris struct nvme_consumer nvme_consumer[NVME_MAX_CONSUMERS];
53038a5ee4SJim Harris #define	INVALID_CONSUMER_ID	0xFFFF
54bb0ec6b3SJim Harris 
55ad697276SJim Harris uma_zone_t	nvme_request_zone;
56cb5b7c13SJim Harris int32_t		nvme_retry_count;
57ad697276SJim Harris 
58bb0ec6b3SJim Harris MALLOC_DEFINE(M_NVME, "nvme", "nvme(4) memory allocations");
59bb0ec6b3SJim Harris 
60bb0ec6b3SJim Harris static int    nvme_probe(device_t);
61bb0ec6b3SJim Harris static int    nvme_attach(device_t);
62bb0ec6b3SJim Harris static int    nvme_detach(device_t);
63c670f31fSNathan Whitehorn static int    nvme_shutdown(device_t);
64bb0ec6b3SJim Harris 
65bb0ec6b3SJim Harris static devclass_t nvme_devclass;
66bb0ec6b3SJim Harris 
67bb0ec6b3SJim Harris static device_method_t nvme_pci_methods[] = {
68bb0ec6b3SJim Harris 	/* Device interface */
69bb0ec6b3SJim Harris 	DEVMETHOD(device_probe,     nvme_probe),
70bb0ec6b3SJim Harris 	DEVMETHOD(device_attach,    nvme_attach),
71bb0ec6b3SJim Harris 	DEVMETHOD(device_detach,    nvme_detach),
72c670f31fSNathan Whitehorn 	DEVMETHOD(device_shutdown,  nvme_shutdown),
73bb0ec6b3SJim Harris 	{ 0, 0 }
74bb0ec6b3SJim Harris };
75bb0ec6b3SJim Harris 
76bb0ec6b3SJim Harris static driver_t nvme_pci_driver = {
77bb0ec6b3SJim Harris 	"nvme",
78bb0ec6b3SJim Harris 	nvme_pci_methods,
79bb0ec6b3SJim Harris 	sizeof(struct nvme_controller),
80bb0ec6b3SJim Harris };
81bb0ec6b3SJim Harris 
8214343799SWarner Losh DRIVER_MODULE(nvme, pci, nvme_pci_driver, nvme_devclass, NULL, NULL);
83bb0ec6b3SJim Harris MODULE_VERSION(nvme, 1);
845a21cd19SKonstantin Belousov MODULE_DEPEND(nvme, cam, 1, 1, 1);
85bb0ec6b3SJim Harris 
86bb0ec6b3SJim Harris static struct _pcsid
87bb0ec6b3SJim Harris {
88eb4929fbSJim Harris 	uint32_t	devid;
89eb4929fbSJim Harris 	int		match_subdevice;
90eb4929fbSJim Harris 	uint16_t	subdevice;
91bb0ec6b3SJim Harris 	const char	*desc;
92ce1ec9c1SWarner Losh 	uint32_t	quirks;
93bb0ec6b3SJim Harris } pci_ids[] = {
94eb4929fbSJim Harris 	{ 0x01118086,		0, 0, "NVMe Controller"  },
95eb4929fbSJim Harris 	{ IDT32_PCI_ID,		0, 0, "IDT NVMe Controller (32 channel)"  },
96eb4929fbSJim Harris 	{ IDT8_PCI_ID,		0, 0, "IDT NVMe Controller (8 channel)" },
97eb4929fbSJim Harris 	{ 0x09538086,		1, 0x3702, "DC P3700 SSD" },
98eb4929fbSJim Harris 	{ 0x09538086,		1, 0x3703, "DC P3700 SSD [2.5\" SFF]" },
99eb4929fbSJim Harris 	{ 0x09538086,		1, 0x3704, "DC P3500 SSD [Add-in Card]" },
100eb4929fbSJim Harris 	{ 0x09538086,		1, 0x3705, "DC P3500 SSD [2.5\" SFF]" },
101eb4929fbSJim Harris 	{ 0x09538086,		1, 0x3709, "DC P3600 SSD [Add-in Card]" },
102eb4929fbSJim Harris 	{ 0x09538086,		1, 0x370a, "DC P3600 SSD [2.5\" SFF]" },
103ce1ec9c1SWarner Losh 	{ 0x00031c58,		0, 0, "HGST SN100",	QUIRK_DELAY_B4_CHK_RDY },
104ce1ec9c1SWarner Losh 	{ 0x00231c58,		0, 0, "WDC SN200",	QUIRK_DELAY_B4_CHK_RDY },
105ce1ec9c1SWarner Losh 	{ 0x05401c5f,		0, 0, "Memblaze Pblaze4", QUIRK_DELAY_B4_CHK_RDY },
106ce1ec9c1SWarner Losh 	{ 0xa821144d,		0, 0, "Samsung PM1725", QUIRK_DELAY_B4_CHK_RDY },
107ce1ec9c1SWarner Losh 	{ 0xa822144d,		0, 0, "Samsung PM1725a", QUIRK_DELAY_B4_CHK_RDY },
10809efa3dfSWarner Losh 	{ 0x01161179,		0, 0, "Toshiba XG5", QUIRK_DISABLE_TIMEOUT },
109eb4929fbSJim Harris 	{ 0x00000000,		0, 0, NULL  }
110bb0ec6b3SJim Harris };
111bb0ec6b3SJim Harris 
112bb0ec6b3SJim Harris static int
113eb4929fbSJim Harris nvme_match(uint32_t devid, uint16_t subdevice, struct _pcsid *ep)
114eb4929fbSJim Harris {
115eb4929fbSJim Harris 	if (devid != ep->devid)
116eb4929fbSJim Harris 		return 0;
117eb4929fbSJim Harris 
118eb4929fbSJim Harris 	if (!ep->match_subdevice)
119eb4929fbSJim Harris 		return 1;
120eb4929fbSJim Harris 
121eb4929fbSJim Harris 	if (subdevice == ep->subdevice)
122eb4929fbSJim Harris 		return 1;
123eb4929fbSJim Harris 	else
124eb4929fbSJim Harris 		return 0;
125eb4929fbSJim Harris }
126eb4929fbSJim Harris 
127eb4929fbSJim Harris static int
128bb0ec6b3SJim Harris nvme_probe (device_t device)
129bb0ec6b3SJim Harris {
130d891b199SJim Harris 	struct _pcsid	*ep;
131eb4929fbSJim Harris 	uint32_t	devid;
132eb4929fbSJim Harris 	uint16_t	subdevice;
133d891b199SJim Harris 
134eb4929fbSJim Harris 	devid = pci_get_devid(device);
135eb4929fbSJim Harris 	subdevice = pci_get_subdevice(device);
136d891b199SJim Harris 	ep = pci_ids;
137d891b199SJim Harris 
138eb4929fbSJim Harris 	while (ep->devid) {
139eb4929fbSJim Harris 		if (nvme_match(devid, subdevice, ep))
140eb4929fbSJim Harris 			break;
141bb0ec6b3SJim Harris 		++ep;
142eb4929fbSJim Harris 	}
143bb0ec6b3SJim Harris 
144bb0ec6b3SJim Harris 	if (ep->desc) {
145bb0ec6b3SJim Harris 		device_set_desc(device, ep->desc);
1467e2fd606SJim Harris 		return (BUS_PROBE_DEFAULT);
147d891b199SJim Harris 	}
148d891b199SJim Harris 
1497e2fd606SJim Harris #if defined(PCIS_STORAGE_NVM)
1507e2fd606SJim Harris 	if (pci_get_class(device)    == PCIC_STORAGE &&
1517e2fd606SJim Harris 	    pci_get_subclass(device) == PCIS_STORAGE_NVM &&
1527e2fd606SJim Harris 	    pci_get_progif(device)   == PCIP_STORAGE_NVM_ENTERPRISE_NVMHCI_1_0) {
1537e2fd606SJim Harris 		device_set_desc(device, "Generic NVMe Device");
1547e2fd606SJim Harris 		return (BUS_PROBE_GENERIC);
1557e2fd606SJim Harris 	}
1567e2fd606SJim Harris #endif
1577e2fd606SJim Harris 
1587e2fd606SJim Harris 	return (ENXIO);
159bb0ec6b3SJim Harris }
160bb0ec6b3SJim Harris 
161bb0ec6b3SJim Harris static void
162ad697276SJim Harris nvme_init(void)
163ad697276SJim Harris {
164038a5ee4SJim Harris 	uint32_t	i;
165038a5ee4SJim Harris 
166ad697276SJim Harris 	nvme_request_zone = uma_zcreate("nvme_request",
167ad697276SJim Harris 	    sizeof(struct nvme_request), NULL, NULL, NULL, NULL, 0, 0);
168038a5ee4SJim Harris 
169038a5ee4SJim Harris 	for (i = 0; i < NVME_MAX_CONSUMERS; i++)
170038a5ee4SJim Harris 		nvme_consumer[i].id = INVALID_CONSUMER_ID;
171ad697276SJim Harris }
172ad697276SJim Harris 
173ad697276SJim Harris SYSINIT(nvme_register, SI_SUB_DRIVERS, SI_ORDER_SECOND, nvme_init, NULL);
174ad697276SJim Harris 
175ad697276SJim Harris static void
176ad697276SJim Harris nvme_uninit(void)
177ad697276SJim Harris {
178ad697276SJim Harris 	uma_zdestroy(nvme_request_zone);
179ad697276SJim Harris }
180ad697276SJim Harris 
181ad697276SJim Harris SYSUNINIT(nvme_unregister, SI_SUB_DRIVERS, SI_ORDER_SECOND, nvme_uninit, NULL);
182ad697276SJim Harris 
183c670f31fSNathan Whitehorn static int
184c670f31fSNathan Whitehorn nvme_shutdown(device_t dev)
185bb0ec6b3SJim Harris {
186bb0ec6b3SJim Harris 	struct nvme_controller	*ctrlr;
187bb0ec6b3SJim Harris 
188c670f31fSNathan Whitehorn 	ctrlr = DEVICE2SOFTC(dev);
18956183abcSJim Harris 	nvme_ctrlr_shutdown(ctrlr);
190bb0ec6b3SJim Harris 
191c670f31fSNathan Whitehorn 	return (0);
192bb0ec6b3SJim Harris }
193bb0ec6b3SJim Harris 
194bb0ec6b3SJim Harris void
195bb0ec6b3SJim Harris nvme_dump_command(struct nvme_command *cmd)
196bb0ec6b3SJim Harris {
1970d787e9bSWojciech Macek 
1984b52061eSDavid E. O'Brien 	printf(
1990d787e9bSWojciech Macek "opc:%x f:%x cid:%x nsid:%x r2:%x r3:%x mptr:%jx prp1:%jx prp2:%jx cdw:%x %x %x %x %x %x\n",
2009544e6dcSChuck Tuffli 	    cmd->opc, cmd->fuse, cmd->cid, le32toh(cmd->nsid),
201bb0ec6b3SJim Harris 	    cmd->rsvd2, cmd->rsvd3,
2020d787e9bSWojciech Macek 	    (uintmax_t)le64toh(cmd->mptr), (uintmax_t)le64toh(cmd->prp1), (uintmax_t)le64toh(cmd->prp2),
2030d787e9bSWojciech Macek 	    le32toh(cmd->cdw10), le32toh(cmd->cdw11), le32toh(cmd->cdw12),
2040d787e9bSWojciech Macek 	    le32toh(cmd->cdw13), le32toh(cmd->cdw14), le32toh(cmd->cdw15));
205bb0ec6b3SJim Harris }
206bb0ec6b3SJim Harris 
207bb0ec6b3SJim Harris void
208bb0ec6b3SJim Harris nvme_dump_completion(struct nvme_completion *cpl)
209bb0ec6b3SJim Harris {
2100d787e9bSWojciech Macek 	uint8_t p, sc, sct, m, dnr;
2110d787e9bSWojciech Macek 	uint16_t status;
2120d787e9bSWojciech Macek 
2130d787e9bSWojciech Macek 	status = le16toh(cpl->status);
2140d787e9bSWojciech Macek 
2150d787e9bSWojciech Macek 	p = NVME_STATUS_GET_P(status);
2160d787e9bSWojciech Macek 	sc = NVME_STATUS_GET_SC(status);
2170d787e9bSWojciech Macek 	sct = NVME_STATUS_GET_SCT(status);
2180d787e9bSWojciech Macek 	m = NVME_STATUS_GET_M(status);
2190d787e9bSWojciech Macek 	dnr = NVME_STATUS_GET_DNR(status);
2200d787e9bSWojciech Macek 
221bb0ec6b3SJim Harris 	printf("cdw0:%08x sqhd:%04x sqid:%04x "
222bb0ec6b3SJim Harris 	    "cid:%04x p:%x sc:%02x sct:%x m:%x dnr:%x\n",
2230d787e9bSWojciech Macek 	    le32toh(cpl->cdw0), le16toh(cpl->sqhd), le16toh(cpl->sqid),
2240d787e9bSWojciech Macek 	    cpl->cid, p, sc, sct, m, dnr);
225bb0ec6b3SJim Harris }
226bb0ec6b3SJim Harris 
227bb0ec6b3SJim Harris static int
228bb0ec6b3SJim Harris nvme_attach(device_t dev)
229bb0ec6b3SJim Harris {
230bb0ec6b3SJim Harris 	struct nvme_controller	*ctrlr = DEVICE2SOFTC(dev);
231bb0ec6b3SJim Harris 	int			status;
232ce1ec9c1SWarner Losh 	struct _pcsid		*ep;
233ce1ec9c1SWarner Losh 	uint32_t		devid;
234ce1ec9c1SWarner Losh 	uint16_t		subdevice;
235ce1ec9c1SWarner Losh 
236ce1ec9c1SWarner Losh 	devid = pci_get_devid(dev);
237ce1ec9c1SWarner Losh 	subdevice = pci_get_subdevice(dev);
238ce1ec9c1SWarner Losh 	ep = pci_ids;
239ce1ec9c1SWarner Losh 	while (ep->devid) {
240ce1ec9c1SWarner Losh 		if (nvme_match(devid, subdevice, ep))
241ce1ec9c1SWarner Losh 			break;
242ce1ec9c1SWarner Losh 		++ep;
243ce1ec9c1SWarner Losh 	}
244ce1ec9c1SWarner Losh 	ctrlr->quirks = ep->quirks;
245bb0ec6b3SJim Harris 
246bb0ec6b3SJim Harris 	status = nvme_ctrlr_construct(ctrlr, dev);
247bb0ec6b3SJim Harris 
2487aa27dbaSJim Harris 	if (status != 0) {
2497aa27dbaSJim Harris 		nvme_ctrlr_destruct(ctrlr, dev);
250bb0ec6b3SJim Harris 		return (status);
2517aa27dbaSJim Harris 	}
252bb0ec6b3SJim Harris 
253bb0ec6b3SJim Harris 	/*
25409efa3dfSWarner Losh 	 * Some drives do not implement the completion timeout feature
25509efa3dfSWarner Losh 	 * correctly. There's a WAR from the manufacturer to just disable it.
25609efa3dfSWarner Losh 	 * The driver wouldn't respond correctly to a timeout anyway.
25709efa3dfSWarner Losh 	 */
25809efa3dfSWarner Losh 	if (ep->quirks & QUIRK_DISABLE_TIMEOUT) {
25909efa3dfSWarner Losh 		int ptr;
26009efa3dfSWarner Losh 		uint16_t devctl2;
26109efa3dfSWarner Losh 
26209efa3dfSWarner Losh 		status = pci_find_cap(dev, PCIY_EXPRESS, &ptr);
26309efa3dfSWarner Losh 		if (status) {
26409efa3dfSWarner Losh 			device_printf(dev, "Can't locate PCIe capability?");
26509efa3dfSWarner Losh 			return (status);
26609efa3dfSWarner Losh 		}
26709efa3dfSWarner Losh 		devctl2 = pci_read_config(dev, ptr + PCIER_DEVICE_CTL2, sizeof(devctl2));
26809efa3dfSWarner Losh 		devctl2 |= PCIEM_CTL2_COMP_TIMO_DISABLE;
26909efa3dfSWarner Losh 		pci_write_config(dev, ptr + PCIER_DEVICE_CTL2, devctl2, sizeof(devctl2));
27009efa3dfSWarner Losh 	}
27109efa3dfSWarner Losh 
27209efa3dfSWarner Losh 	/*
273abb61405SWarner Losh 	 * Enable busmastering so the completion status messages can
274abb61405SWarner Losh 	 * be busmastered back to the host.
275abb61405SWarner Losh 	 */
276abb61405SWarner Losh 	pci_enable_busmaster(dev);
277abb61405SWarner Losh 
278abb61405SWarner Losh 	/*
279bb0ec6b3SJim Harris 	 * Reset controller twice to ensure we do a transition from cc.en==1
280bb0ec6b3SJim Harris 	 *  to cc.en==0.  This is because we don't really know what status
281bb0ec6b3SJim Harris 	 *  the controller was left in when boot handed off to OS.
282bb0ec6b3SJim Harris 	 */
283b846efd7SJim Harris 	status = nvme_ctrlr_hw_reset(ctrlr);
2847aa27dbaSJim Harris 	if (status != 0) {
2857aa27dbaSJim Harris 		nvme_ctrlr_destruct(ctrlr, dev);
286bb0ec6b3SJim Harris 		return (status);
2877aa27dbaSJim Harris 	}
288bb0ec6b3SJim Harris 
289b846efd7SJim Harris 	status = nvme_ctrlr_hw_reset(ctrlr);
2907aa27dbaSJim Harris 	if (status != 0) {
2917aa27dbaSJim Harris 		nvme_ctrlr_destruct(ctrlr, dev);
292bb0ec6b3SJim Harris 		return (status);
2937aa27dbaSJim Harris 	}
294bb0ec6b3SJim Harris 
295be34f216SJim Harris 	ctrlr->config_hook.ich_func = nvme_ctrlr_start_config_hook;
296bb0ec6b3SJim Harris 	ctrlr->config_hook.ich_arg = ctrlr;
297bb0ec6b3SJim Harris 
298bb0ec6b3SJim Harris 	config_intrhook_establish(&ctrlr->config_hook);
299bb0ec6b3SJim Harris 
300bb0ec6b3SJim Harris 	return (0);
301bb0ec6b3SJim Harris }
302bb0ec6b3SJim Harris 
303bb0ec6b3SJim Harris static int
304bb0ec6b3SJim Harris nvme_detach (device_t dev)
305bb0ec6b3SJim Harris {
306bb0ec6b3SJim Harris 	struct nvme_controller	*ctrlr = DEVICE2SOFTC(dev);
307bb0ec6b3SJim Harris 
308990e741cSJim Harris 	nvme_ctrlr_destruct(ctrlr, dev);
309eb32b874SJim Harris 	pci_disable_busmaster(dev);
310bb0ec6b3SJim Harris 	return (0);
311bb0ec6b3SJim Harris }
312bb0ec6b3SJim Harris 
313bb0ec6b3SJim Harris static void
314496a2752SJim Harris nvme_notify(struct nvme_consumer *cons,
315496a2752SJim Harris 	    struct nvme_controller *ctrlr)
316bb0ec6b3SJim Harris {
317038a5ee4SJim Harris 	struct nvme_namespace	*ns;
318038a5ee4SJim Harris 	void			*ctrlr_cookie;
319496a2752SJim Harris 	int			cmpset, ns_idx;
320bb0ec6b3SJim Harris 
321496a2752SJim Harris 	/*
322496a2752SJim Harris 	 * The consumer may register itself after the nvme devices
323496a2752SJim Harris 	 *  have registered with the kernel, but before the
324496a2752SJim Harris 	 *  driver has completed initialization.  In that case,
325496a2752SJim Harris 	 *  return here, and when initialization completes, the
326496a2752SJim Harris 	 *  controller will make sure the consumer gets notified.
327496a2752SJim Harris 	 */
328496a2752SJim Harris 	if (!ctrlr->is_initialized)
329bb0ec6b3SJim Harris 		return;
330bb0ec6b3SJim Harris 
331496a2752SJim Harris 	cmpset = atomic_cmpset_32(&ctrlr->notification_sent, 0, 1);
332496a2752SJim Harris 
333496a2752SJim Harris 	if (cmpset == 0)
334496a2752SJim Harris 		return;
335496a2752SJim Harris 
336038a5ee4SJim Harris 	if (cons->ctrlr_fn != NULL)
337038a5ee4SJim Harris 		ctrlr_cookie = (*cons->ctrlr_fn)(ctrlr);
338038a5ee4SJim Harris 	else
339038a5ee4SJim Harris 		ctrlr_cookie = NULL;
340038a5ee4SJim Harris 	ctrlr->cons_cookie[cons->id] = ctrlr_cookie;
341086d23cfSJim Harris 	if (ctrlr->is_failed) {
342086d23cfSJim Harris 		if (cons->fail_fn != NULL)
343086d23cfSJim Harris 			(*cons->fail_fn)(ctrlr_cookie);
344086d23cfSJim Harris 		/*
345086d23cfSJim Harris 		 * Do not notify consumers about the namespaces of a
346086d23cfSJim Harris 		 *  failed controller.
347086d23cfSJim Harris 		 */
348496a2752SJim Harris 		return;
349086d23cfSJim Harris 	}
350a8a18dd5SWarner Losh 	for (ns_idx = 0; ns_idx < min(ctrlr->cdata.nn, NVME_MAX_NAMESPACES); ns_idx++) {
351038a5ee4SJim Harris 		ns = &ctrlr->ns[ns_idx];
352a8a18dd5SWarner Losh 		if (ns->data.nsze == 0)
353a8a18dd5SWarner Losh 			continue;
354038a5ee4SJim Harris 		if (cons->ns_fn != NULL)
355038a5ee4SJim Harris 			ns->cons_cookie[cons->id] =
356038a5ee4SJim Harris 			    (*cons->ns_fn)(ns, ctrlr_cookie);
357038a5ee4SJim Harris 	}
358bb0ec6b3SJim Harris }
359bb0ec6b3SJim Harris 
360496a2752SJim Harris void
361496a2752SJim Harris nvme_notify_new_controller(struct nvme_controller *ctrlr)
362496a2752SJim Harris {
363496a2752SJim Harris 	int i;
364496a2752SJim Harris 
365496a2752SJim Harris 	for (i = 0; i < NVME_MAX_CONSUMERS; i++) {
366496a2752SJim Harris 		if (nvme_consumer[i].id != INVALID_CONSUMER_ID) {
367496a2752SJim Harris 			nvme_notify(&nvme_consumer[i], ctrlr);
368496a2752SJim Harris 		}
369496a2752SJim Harris 	}
370496a2752SJim Harris }
371496a2752SJim Harris 
372496a2752SJim Harris static void
373496a2752SJim Harris nvme_notify_new_consumer(struct nvme_consumer *cons)
374496a2752SJim Harris {
375496a2752SJim Harris 	device_t		*devlist;
376496a2752SJim Harris 	struct nvme_controller	*ctrlr;
377496a2752SJim Harris 	int			dev_idx, devcount;
378496a2752SJim Harris 
379496a2752SJim Harris 	if (devclass_get_devices(nvme_devclass, &devlist, &devcount))
380496a2752SJim Harris 		return;
381496a2752SJim Harris 
382496a2752SJim Harris 	for (dev_idx = 0; dev_idx < devcount; dev_idx++) {
383496a2752SJim Harris 		ctrlr = DEVICE2SOFTC(devlist[dev_idx]);
384496a2752SJim Harris 		nvme_notify(cons, ctrlr);
385496a2752SJim Harris 	}
386496a2752SJim Harris 
387bb0ec6b3SJim Harris 	free(devlist, M_TEMP);
388bb0ec6b3SJim Harris }
389bb0ec6b3SJim Harris 
390038a5ee4SJim Harris void
391038a5ee4SJim Harris nvme_notify_async_consumers(struct nvme_controller *ctrlr,
3920d7e13ecSJim Harris 			    const struct nvme_completion *async_cpl,
3930d7e13ecSJim Harris 			    uint32_t log_page_id, void *log_page_buffer,
3940d7e13ecSJim Harris 			    uint32_t log_page_size)
395038a5ee4SJim Harris {
396038a5ee4SJim Harris 	struct nvme_consumer	*cons;
397038a5ee4SJim Harris 	uint32_t		i;
398038a5ee4SJim Harris 
399038a5ee4SJim Harris 	for (i = 0; i < NVME_MAX_CONSUMERS; i++) {
400038a5ee4SJim Harris 		cons = &nvme_consumer[i];
401038a5ee4SJim Harris 		if (cons->id != INVALID_CONSUMER_ID && cons->async_fn != NULL)
4020d7e13ecSJim Harris 			(*cons->async_fn)(ctrlr->cons_cookie[i], async_cpl,
4030d7e13ecSJim Harris 			    log_page_id, log_page_buffer, log_page_size);
404038a5ee4SJim Harris 	}
405038a5ee4SJim Harris }
406038a5ee4SJim Harris 
407232e2edbSJim Harris void
408232e2edbSJim Harris nvme_notify_fail_consumers(struct nvme_controller *ctrlr)
409232e2edbSJim Harris {
410232e2edbSJim Harris 	struct nvme_consumer	*cons;
411232e2edbSJim Harris 	uint32_t		i;
412232e2edbSJim Harris 
4130e1fd2ddSJim Harris 	/*
4140e1fd2ddSJim Harris 	 * This controller failed during initialization (i.e. IDENTIFY
4150e1fd2ddSJim Harris 	 *  command failed or timed out).  Do not notify any nvme
4160e1fd2ddSJim Harris 	 *  consumers of the failure here, since the consumer does not
4170e1fd2ddSJim Harris 	 *  even know about the controller yet.
4180e1fd2ddSJim Harris 	 */
4190e1fd2ddSJim Harris 	if (!ctrlr->is_initialized)
4200e1fd2ddSJim Harris 		return;
4210e1fd2ddSJim Harris 
422232e2edbSJim Harris 	for (i = 0; i < NVME_MAX_CONSUMERS; i++) {
423232e2edbSJim Harris 		cons = &nvme_consumer[i];
424232e2edbSJim Harris 		if (cons->id != INVALID_CONSUMER_ID && cons->fail_fn != NULL)
425232e2edbSJim Harris 			cons->fail_fn(ctrlr->cons_cookie[i]);
426232e2edbSJim Harris 	}
427232e2edbSJim Harris }
428232e2edbSJim Harris 
429f439e3a4SAlexander Motin void
430f439e3a4SAlexander Motin nvme_notify_ns(struct nvme_controller *ctrlr, int nsid)
431f439e3a4SAlexander Motin {
432f439e3a4SAlexander Motin 	struct nvme_consumer	*cons;
433f439e3a4SAlexander Motin 	struct nvme_namespace	*ns = &ctrlr->ns[nsid - 1];
434f439e3a4SAlexander Motin 	uint32_t		i;
435f439e3a4SAlexander Motin 
436f439e3a4SAlexander Motin 	if (!ctrlr->is_initialized)
437f439e3a4SAlexander Motin 		return;
438f439e3a4SAlexander Motin 
439f439e3a4SAlexander Motin 	for (i = 0; i < NVME_MAX_CONSUMERS; i++) {
440f439e3a4SAlexander Motin 		cons = &nvme_consumer[i];
441f439e3a4SAlexander Motin 		if (cons->id != INVALID_CONSUMER_ID && cons->ns_fn != NULL)
442f439e3a4SAlexander Motin 			ns->cons_cookie[cons->id] =
443f439e3a4SAlexander Motin 			    (*cons->ns_fn)(ns, ctrlr->cons_cookie[cons->id]);
444f439e3a4SAlexander Motin 	}
445f439e3a4SAlexander Motin }
446f439e3a4SAlexander Motin 
447bb0ec6b3SJim Harris struct nvme_consumer *
448038a5ee4SJim Harris nvme_register_consumer(nvme_cons_ns_fn_t ns_fn, nvme_cons_ctrlr_fn_t ctrlr_fn,
449232e2edbSJim Harris 		       nvme_cons_async_fn_t async_fn,
450232e2edbSJim Harris 		       nvme_cons_fail_fn_t fail_fn)
451bb0ec6b3SJim Harris {
452bb0ec6b3SJim Harris 	int i;
453bb0ec6b3SJim Harris 
454bb0ec6b3SJim Harris 	/*
455*204498d7SWarner Losh 	 * TODO: add locking around consumer registration.
456bb0ec6b3SJim Harris 	 */
457bb0ec6b3SJim Harris 	for (i = 0; i < NVME_MAX_CONSUMERS; i++)
458038a5ee4SJim Harris 		if (nvme_consumer[i].id == INVALID_CONSUMER_ID) {
459038a5ee4SJim Harris 			nvme_consumer[i].id = i;
460038a5ee4SJim Harris 			nvme_consumer[i].ns_fn = ns_fn;
461038a5ee4SJim Harris 			nvme_consumer[i].ctrlr_fn = ctrlr_fn;
462038a5ee4SJim Harris 			nvme_consumer[i].async_fn = async_fn;
463232e2edbSJim Harris 			nvme_consumer[i].fail_fn = fail_fn;
464bb0ec6b3SJim Harris 
465496a2752SJim Harris 			nvme_notify_new_consumer(&nvme_consumer[i]);
466bb0ec6b3SJim Harris 			return (&nvme_consumer[i]);
467bb0ec6b3SJim Harris 		}
468bb0ec6b3SJim Harris 
469bb0ec6b3SJim Harris 	printf("nvme(4): consumer not registered - no slots available\n");
470bb0ec6b3SJim Harris 	return (NULL);
471bb0ec6b3SJim Harris }
472bb0ec6b3SJim Harris 
473bb0ec6b3SJim Harris void
474bb0ec6b3SJim Harris nvme_unregister_consumer(struct nvme_consumer *consumer)
475bb0ec6b3SJim Harris {
476bb0ec6b3SJim Harris 
477038a5ee4SJim Harris 	consumer->id = INVALID_CONSUMER_ID;
478bb0ec6b3SJim Harris }
479bb0ec6b3SJim Harris 
480955910a9SJim Harris void
481955910a9SJim Harris nvme_completion_poll_cb(void *arg, const struct nvme_completion *cpl)
482955910a9SJim Harris {
483955910a9SJim Harris 	struct nvme_completion_poll_status	*status = arg;
484955910a9SJim Harris 
485955910a9SJim Harris 	/*
486955910a9SJim Harris 	 * Copy status into the argument passed by the caller, so that
487955910a9SJim Harris 	 *  the caller can check the status to determine if the
488955910a9SJim Harris 	 *  the request passed or failed.
489955910a9SJim Harris 	 */
490955910a9SJim Harris 	memcpy(&status->cpl, cpl, sizeof(*cpl));
49129077eb4SWarner Losh 	atomic_store_rel_int(&status->done, 1);
492955910a9SJim Harris }
493