xref: /freebsd/sys/dev/nvme/nvme.c (revision f439e3a4ff12c411345fd653e9ca4d75b36ada5d)
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);
64e1e84e74SJim Harris static int    nvme_modevent(module_t mod, int type, void *arg);
65bb0ec6b3SJim Harris 
66bb0ec6b3SJim Harris static devclass_t nvme_devclass;
67bb0ec6b3SJim Harris 
68bb0ec6b3SJim Harris static device_method_t nvme_pci_methods[] = {
69bb0ec6b3SJim Harris 	/* Device interface */
70bb0ec6b3SJim Harris 	DEVMETHOD(device_probe,     nvme_probe),
71bb0ec6b3SJim Harris 	DEVMETHOD(device_attach,    nvme_attach),
72bb0ec6b3SJim Harris 	DEVMETHOD(device_detach,    nvme_detach),
73c670f31fSNathan Whitehorn 	DEVMETHOD(device_shutdown,  nvme_shutdown),
74bb0ec6b3SJim Harris 	{ 0, 0 }
75bb0ec6b3SJim Harris };
76bb0ec6b3SJim Harris 
77bb0ec6b3SJim Harris static driver_t nvme_pci_driver = {
78bb0ec6b3SJim Harris 	"nvme",
79bb0ec6b3SJim Harris 	nvme_pci_methods,
80bb0ec6b3SJim Harris 	sizeof(struct nvme_controller),
81bb0ec6b3SJim Harris };
82bb0ec6b3SJim Harris 
83e1e84e74SJim Harris DRIVER_MODULE(nvme, pci, nvme_pci_driver, nvme_devclass, nvme_modevent, 0);
84bb0ec6b3SJim Harris MODULE_VERSION(nvme, 1);
855a21cd19SKonstantin Belousov MODULE_DEPEND(nvme, cam, 1, 1, 1);
86bb0ec6b3SJim Harris 
87bb0ec6b3SJim Harris static struct _pcsid
88bb0ec6b3SJim Harris {
89eb4929fbSJim Harris 	uint32_t	devid;
90eb4929fbSJim Harris 	int		match_subdevice;
91eb4929fbSJim Harris 	uint16_t	subdevice;
92bb0ec6b3SJim Harris 	const char	*desc;
93ce1ec9c1SWarner Losh 	uint32_t	quirks;
94bb0ec6b3SJim Harris } pci_ids[] = {
95eb4929fbSJim Harris 	{ 0x01118086,		0, 0, "NVMe Controller"  },
96eb4929fbSJim Harris 	{ IDT32_PCI_ID,		0, 0, "IDT NVMe Controller (32 channel)"  },
97eb4929fbSJim Harris 	{ IDT8_PCI_ID,		0, 0, "IDT NVMe Controller (8 channel)" },
98eb4929fbSJim Harris 	{ 0x09538086,		1, 0x3702, "DC P3700 SSD" },
99eb4929fbSJim Harris 	{ 0x09538086,		1, 0x3703, "DC P3700 SSD [2.5\" SFF]" },
100eb4929fbSJim Harris 	{ 0x09538086,		1, 0x3704, "DC P3500 SSD [Add-in Card]" },
101eb4929fbSJim Harris 	{ 0x09538086,		1, 0x3705, "DC P3500 SSD [2.5\" SFF]" },
102eb4929fbSJim Harris 	{ 0x09538086,		1, 0x3709, "DC P3600 SSD [Add-in Card]" },
103eb4929fbSJim Harris 	{ 0x09538086,		1, 0x370a, "DC P3600 SSD [2.5\" SFF]" },
104ce1ec9c1SWarner Losh 	{ 0x00031c58,		0, 0, "HGST SN100",	QUIRK_DELAY_B4_CHK_RDY },
105ce1ec9c1SWarner Losh 	{ 0x00231c58,		0, 0, "WDC SN200",	QUIRK_DELAY_B4_CHK_RDY },
106ce1ec9c1SWarner Losh 	{ 0x05401c5f,		0, 0, "Memblaze Pblaze4", QUIRK_DELAY_B4_CHK_RDY },
107ce1ec9c1SWarner Losh 	{ 0xa821144d,		0, 0, "Samsung PM1725", QUIRK_DELAY_B4_CHK_RDY },
108ce1ec9c1SWarner Losh 	{ 0xa822144d,		0, 0, "Samsung PM1725a", QUIRK_DELAY_B4_CHK_RDY },
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 
183ad697276SJim Harris static void
184bb0ec6b3SJim Harris nvme_load(void)
185bb0ec6b3SJim Harris {
186bb0ec6b3SJim Harris }
187bb0ec6b3SJim Harris 
188bb0ec6b3SJim Harris static void
189bb0ec6b3SJim Harris nvme_unload(void)
190bb0ec6b3SJim Harris {
191bb0ec6b3SJim Harris }
192bb0ec6b3SJim Harris 
193c670f31fSNathan Whitehorn static int
194c670f31fSNathan Whitehorn nvme_shutdown(device_t dev)
195bb0ec6b3SJim Harris {
196bb0ec6b3SJim Harris 	struct nvme_controller	*ctrlr;
197bb0ec6b3SJim Harris 
198c670f31fSNathan Whitehorn 	ctrlr = DEVICE2SOFTC(dev);
19956183abcSJim Harris 	nvme_ctrlr_shutdown(ctrlr);
200bb0ec6b3SJim Harris 
201c670f31fSNathan Whitehorn 	return (0);
202bb0ec6b3SJim Harris }
203bb0ec6b3SJim Harris 
204bb0ec6b3SJim Harris static int
205bb0ec6b3SJim Harris nvme_modevent(module_t mod, int type, void *arg)
206bb0ec6b3SJim Harris {
207bb0ec6b3SJim Harris 
208bb0ec6b3SJim Harris 	switch (type) {
209bb0ec6b3SJim Harris 	case MOD_LOAD:
210bb0ec6b3SJim Harris 		nvme_load();
211bb0ec6b3SJim Harris 		break;
212bb0ec6b3SJim Harris 	case MOD_UNLOAD:
213bb0ec6b3SJim Harris 		nvme_unload();
214bb0ec6b3SJim Harris 		break;
215bb0ec6b3SJim Harris 	default:
216bb0ec6b3SJim Harris 		break;
217bb0ec6b3SJim Harris 	}
218bb0ec6b3SJim Harris 
219bb0ec6b3SJim Harris 	return (0);
220bb0ec6b3SJim Harris }
221bb0ec6b3SJim Harris 
222bb0ec6b3SJim Harris void
223bb0ec6b3SJim Harris nvme_dump_command(struct nvme_command *cmd)
224bb0ec6b3SJim Harris {
2250d787e9bSWojciech Macek 	uint8_t opc, fuse;
2260d787e9bSWojciech Macek 
2270d787e9bSWojciech Macek 	opc = (cmd->opc_fuse >> NVME_CMD_OPC_SHIFT) & NVME_CMD_OPC_MASK;
2280d787e9bSWojciech Macek 	fuse = (cmd->opc_fuse >> NVME_CMD_FUSE_SHIFT) & NVME_CMD_FUSE_MASK;
2290d787e9bSWojciech Macek 
2304b52061eSDavid E. O'Brien 	printf(
2310d787e9bSWojciech 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",
2320d787e9bSWojciech Macek 	    opc, fuse, cmd->cid, le32toh(cmd->nsid),
233bb0ec6b3SJim Harris 	    cmd->rsvd2, cmd->rsvd3,
2340d787e9bSWojciech Macek 	    (uintmax_t)le64toh(cmd->mptr), (uintmax_t)le64toh(cmd->prp1), (uintmax_t)le64toh(cmd->prp2),
2350d787e9bSWojciech Macek 	    le32toh(cmd->cdw10), le32toh(cmd->cdw11), le32toh(cmd->cdw12),
2360d787e9bSWojciech Macek 	    le32toh(cmd->cdw13), le32toh(cmd->cdw14), le32toh(cmd->cdw15));
237bb0ec6b3SJim Harris }
238bb0ec6b3SJim Harris 
239bb0ec6b3SJim Harris void
240bb0ec6b3SJim Harris nvme_dump_completion(struct nvme_completion *cpl)
241bb0ec6b3SJim Harris {
2420d787e9bSWojciech Macek 	uint8_t p, sc, sct, m, dnr;
2430d787e9bSWojciech Macek 	uint16_t status;
2440d787e9bSWojciech Macek 
2450d787e9bSWojciech Macek 	status = le16toh(cpl->status);
2460d787e9bSWojciech Macek 
2470d787e9bSWojciech Macek 	p = NVME_STATUS_GET_P(status);
2480d787e9bSWojciech Macek 	sc = NVME_STATUS_GET_SC(status);
2490d787e9bSWojciech Macek 	sct = NVME_STATUS_GET_SCT(status);
2500d787e9bSWojciech Macek 	m = NVME_STATUS_GET_M(status);
2510d787e9bSWojciech Macek 	dnr = NVME_STATUS_GET_DNR(status);
2520d787e9bSWojciech Macek 
253bb0ec6b3SJim Harris 	printf("cdw0:%08x sqhd:%04x sqid:%04x "
254bb0ec6b3SJim Harris 	    "cid:%04x p:%x sc:%02x sct:%x m:%x dnr:%x\n",
2550d787e9bSWojciech Macek 	    le32toh(cpl->cdw0), le16toh(cpl->sqhd), le16toh(cpl->sqid),
2560d787e9bSWojciech Macek 	    cpl->cid, p, sc, sct, m, dnr);
257bb0ec6b3SJim Harris }
258bb0ec6b3SJim Harris 
259bb0ec6b3SJim Harris static int
260bb0ec6b3SJim Harris nvme_attach(device_t dev)
261bb0ec6b3SJim Harris {
262bb0ec6b3SJim Harris 	struct nvme_controller	*ctrlr = DEVICE2SOFTC(dev);
263bb0ec6b3SJim Harris 	int			status;
264ce1ec9c1SWarner Losh 	struct _pcsid		*ep;
265ce1ec9c1SWarner Losh 	uint32_t		devid;
266ce1ec9c1SWarner Losh 	uint16_t		subdevice;
267ce1ec9c1SWarner Losh 
268ce1ec9c1SWarner Losh 	devid = pci_get_devid(dev);
269ce1ec9c1SWarner Losh 	subdevice = pci_get_subdevice(dev);
270ce1ec9c1SWarner Losh 	ep = pci_ids;
271ce1ec9c1SWarner Losh 	while (ep->devid) {
272ce1ec9c1SWarner Losh 		if (nvme_match(devid, subdevice, ep))
273ce1ec9c1SWarner Losh 			break;
274ce1ec9c1SWarner Losh 		++ep;
275ce1ec9c1SWarner Losh 	}
276ce1ec9c1SWarner Losh 	ctrlr->quirks = ep->quirks;
277bb0ec6b3SJim Harris 
278bb0ec6b3SJim Harris 	status = nvme_ctrlr_construct(ctrlr, dev);
279bb0ec6b3SJim Harris 
2807aa27dbaSJim Harris 	if (status != 0) {
2817aa27dbaSJim Harris 		nvme_ctrlr_destruct(ctrlr, dev);
282bb0ec6b3SJim Harris 		return (status);
2837aa27dbaSJim Harris 	}
284bb0ec6b3SJim Harris 
285bb0ec6b3SJim Harris 	/*
286abb61405SWarner Losh 	 * Enable busmastering so the completion status messages can
287abb61405SWarner Losh 	 * be busmastered back to the host.
288abb61405SWarner Losh 	 */
289abb61405SWarner Losh 	pci_enable_busmaster(dev);
290abb61405SWarner Losh 
291abb61405SWarner Losh 	/*
292bb0ec6b3SJim Harris 	 * Reset controller twice to ensure we do a transition from cc.en==1
293bb0ec6b3SJim Harris 	 *  to cc.en==0.  This is because we don't really know what status
294bb0ec6b3SJim Harris 	 *  the controller was left in when boot handed off to OS.
295bb0ec6b3SJim Harris 	 */
296b846efd7SJim Harris 	status = nvme_ctrlr_hw_reset(ctrlr);
2977aa27dbaSJim Harris 	if (status != 0) {
2987aa27dbaSJim Harris 		nvme_ctrlr_destruct(ctrlr, dev);
299bb0ec6b3SJim Harris 		return (status);
3007aa27dbaSJim Harris 	}
301bb0ec6b3SJim Harris 
302b846efd7SJim Harris 	status = nvme_ctrlr_hw_reset(ctrlr);
3037aa27dbaSJim Harris 	if (status != 0) {
3047aa27dbaSJim Harris 		nvme_ctrlr_destruct(ctrlr, dev);
305bb0ec6b3SJim Harris 		return (status);
3067aa27dbaSJim Harris 	}
307bb0ec6b3SJim Harris 
308be34f216SJim Harris 	ctrlr->config_hook.ich_func = nvme_ctrlr_start_config_hook;
309bb0ec6b3SJim Harris 	ctrlr->config_hook.ich_arg = ctrlr;
310bb0ec6b3SJim Harris 
311bb0ec6b3SJim Harris 	config_intrhook_establish(&ctrlr->config_hook);
312bb0ec6b3SJim Harris 
313bb0ec6b3SJim Harris 	return (0);
314bb0ec6b3SJim Harris }
315bb0ec6b3SJim Harris 
316bb0ec6b3SJim Harris static int
317bb0ec6b3SJim Harris nvme_detach (device_t dev)
318bb0ec6b3SJim Harris {
319bb0ec6b3SJim Harris 	struct nvme_controller	*ctrlr = DEVICE2SOFTC(dev);
320bb0ec6b3SJim Harris 
321990e741cSJim Harris 	nvme_ctrlr_destruct(ctrlr, dev);
322eb32b874SJim Harris 	pci_disable_busmaster(dev);
323bb0ec6b3SJim Harris 	return (0);
324bb0ec6b3SJim Harris }
325bb0ec6b3SJim Harris 
326bb0ec6b3SJim Harris static void
327496a2752SJim Harris nvme_notify(struct nvme_consumer *cons,
328496a2752SJim Harris 	    struct nvme_controller *ctrlr)
329bb0ec6b3SJim Harris {
330038a5ee4SJim Harris 	struct nvme_namespace	*ns;
331038a5ee4SJim Harris 	void			*ctrlr_cookie;
332496a2752SJim Harris 	int			cmpset, ns_idx;
333bb0ec6b3SJim Harris 
334496a2752SJim Harris 	/*
335496a2752SJim Harris 	 * The consumer may register itself after the nvme devices
336496a2752SJim Harris 	 *  have registered with the kernel, but before the
337496a2752SJim Harris 	 *  driver has completed initialization.  In that case,
338496a2752SJim Harris 	 *  return here, and when initialization completes, the
339496a2752SJim Harris 	 *  controller will make sure the consumer gets notified.
340496a2752SJim Harris 	 */
341496a2752SJim Harris 	if (!ctrlr->is_initialized)
342bb0ec6b3SJim Harris 		return;
343bb0ec6b3SJim Harris 
344496a2752SJim Harris 	cmpset = atomic_cmpset_32(&ctrlr->notification_sent, 0, 1);
345496a2752SJim Harris 
346496a2752SJim Harris 	if (cmpset == 0)
347496a2752SJim Harris 		return;
348496a2752SJim Harris 
349038a5ee4SJim Harris 	if (cons->ctrlr_fn != NULL)
350038a5ee4SJim Harris 		ctrlr_cookie = (*cons->ctrlr_fn)(ctrlr);
351038a5ee4SJim Harris 	else
352038a5ee4SJim Harris 		ctrlr_cookie = NULL;
353038a5ee4SJim Harris 	ctrlr->cons_cookie[cons->id] = ctrlr_cookie;
354086d23cfSJim Harris 	if (ctrlr->is_failed) {
355086d23cfSJim Harris 		if (cons->fail_fn != NULL)
356086d23cfSJim Harris 			(*cons->fail_fn)(ctrlr_cookie);
357086d23cfSJim Harris 		/*
358086d23cfSJim Harris 		 * Do not notify consumers about the namespaces of a
359086d23cfSJim Harris 		 *  failed controller.
360086d23cfSJim Harris 		 */
361496a2752SJim Harris 		return;
362086d23cfSJim Harris 	}
363a8a18dd5SWarner Losh 	for (ns_idx = 0; ns_idx < min(ctrlr->cdata.nn, NVME_MAX_NAMESPACES); ns_idx++) {
364038a5ee4SJim Harris 		ns = &ctrlr->ns[ns_idx];
365a8a18dd5SWarner Losh 		if (ns->data.nsze == 0)
366a8a18dd5SWarner Losh 			continue;
367038a5ee4SJim Harris 		if (cons->ns_fn != NULL)
368038a5ee4SJim Harris 			ns->cons_cookie[cons->id] =
369038a5ee4SJim Harris 			    (*cons->ns_fn)(ns, ctrlr_cookie);
370038a5ee4SJim Harris 	}
371bb0ec6b3SJim Harris }
372bb0ec6b3SJim Harris 
373496a2752SJim Harris void
374496a2752SJim Harris nvme_notify_new_controller(struct nvme_controller *ctrlr)
375496a2752SJim Harris {
376496a2752SJim Harris 	int i;
377496a2752SJim Harris 
378496a2752SJim Harris 	for (i = 0; i < NVME_MAX_CONSUMERS; i++) {
379496a2752SJim Harris 		if (nvme_consumer[i].id != INVALID_CONSUMER_ID) {
380496a2752SJim Harris 			nvme_notify(&nvme_consumer[i], ctrlr);
381496a2752SJim Harris 		}
382496a2752SJim Harris 	}
383496a2752SJim Harris }
384496a2752SJim Harris 
385496a2752SJim Harris static void
386496a2752SJim Harris nvme_notify_new_consumer(struct nvme_consumer *cons)
387496a2752SJim Harris {
388496a2752SJim Harris 	device_t		*devlist;
389496a2752SJim Harris 	struct nvme_controller	*ctrlr;
390496a2752SJim Harris 	int			dev_idx, devcount;
391496a2752SJim Harris 
392496a2752SJim Harris 	if (devclass_get_devices(nvme_devclass, &devlist, &devcount))
393496a2752SJim Harris 		return;
394496a2752SJim Harris 
395496a2752SJim Harris 	for (dev_idx = 0; dev_idx < devcount; dev_idx++) {
396496a2752SJim Harris 		ctrlr = DEVICE2SOFTC(devlist[dev_idx]);
397496a2752SJim Harris 		nvme_notify(cons, ctrlr);
398496a2752SJim Harris 	}
399496a2752SJim Harris 
400bb0ec6b3SJim Harris 	free(devlist, M_TEMP);
401bb0ec6b3SJim Harris }
402bb0ec6b3SJim Harris 
403038a5ee4SJim Harris void
404038a5ee4SJim Harris nvme_notify_async_consumers(struct nvme_controller *ctrlr,
4050d7e13ecSJim Harris 			    const struct nvme_completion *async_cpl,
4060d7e13ecSJim Harris 			    uint32_t log_page_id, void *log_page_buffer,
4070d7e13ecSJim Harris 			    uint32_t log_page_size)
408038a5ee4SJim Harris {
409038a5ee4SJim Harris 	struct nvme_consumer	*cons;
410038a5ee4SJim Harris 	uint32_t		i;
411038a5ee4SJim Harris 
412038a5ee4SJim Harris 	for (i = 0; i < NVME_MAX_CONSUMERS; i++) {
413038a5ee4SJim Harris 		cons = &nvme_consumer[i];
414038a5ee4SJim Harris 		if (cons->id != INVALID_CONSUMER_ID && cons->async_fn != NULL)
4150d7e13ecSJim Harris 			(*cons->async_fn)(ctrlr->cons_cookie[i], async_cpl,
4160d7e13ecSJim Harris 			    log_page_id, log_page_buffer, log_page_size);
417038a5ee4SJim Harris 	}
418038a5ee4SJim Harris }
419038a5ee4SJim Harris 
420232e2edbSJim Harris void
421232e2edbSJim Harris nvme_notify_fail_consumers(struct nvme_controller *ctrlr)
422232e2edbSJim Harris {
423232e2edbSJim Harris 	struct nvme_consumer	*cons;
424232e2edbSJim Harris 	uint32_t		i;
425232e2edbSJim Harris 
4260e1fd2ddSJim Harris 	/*
4270e1fd2ddSJim Harris 	 * This controller failed during initialization (i.e. IDENTIFY
4280e1fd2ddSJim Harris 	 *  command failed or timed out).  Do not notify any nvme
4290e1fd2ddSJim Harris 	 *  consumers of the failure here, since the consumer does not
4300e1fd2ddSJim Harris 	 *  even know about the controller yet.
4310e1fd2ddSJim Harris 	 */
4320e1fd2ddSJim Harris 	if (!ctrlr->is_initialized)
4330e1fd2ddSJim Harris 		return;
4340e1fd2ddSJim Harris 
435232e2edbSJim Harris 	for (i = 0; i < NVME_MAX_CONSUMERS; i++) {
436232e2edbSJim Harris 		cons = &nvme_consumer[i];
437232e2edbSJim Harris 		if (cons->id != INVALID_CONSUMER_ID && cons->fail_fn != NULL)
438232e2edbSJim Harris 			cons->fail_fn(ctrlr->cons_cookie[i]);
439232e2edbSJim Harris 	}
440232e2edbSJim Harris }
441232e2edbSJim Harris 
442*f439e3a4SAlexander Motin void
443*f439e3a4SAlexander Motin nvme_notify_ns(struct nvme_controller *ctrlr, int nsid)
444*f439e3a4SAlexander Motin {
445*f439e3a4SAlexander Motin 	struct nvme_consumer	*cons;
446*f439e3a4SAlexander Motin 	struct nvme_namespace	*ns = &ctrlr->ns[nsid - 1];
447*f439e3a4SAlexander Motin 	uint32_t		i;
448*f439e3a4SAlexander Motin 
449*f439e3a4SAlexander Motin 	if (!ctrlr->is_initialized)
450*f439e3a4SAlexander Motin 		return;
451*f439e3a4SAlexander Motin 
452*f439e3a4SAlexander Motin 	for (i = 0; i < NVME_MAX_CONSUMERS; i++) {
453*f439e3a4SAlexander Motin 		cons = &nvme_consumer[i];
454*f439e3a4SAlexander Motin 		if (cons->id != INVALID_CONSUMER_ID && cons->ns_fn != NULL)
455*f439e3a4SAlexander Motin 			ns->cons_cookie[cons->id] =
456*f439e3a4SAlexander Motin 			    (*cons->ns_fn)(ns, ctrlr->cons_cookie[cons->id]);
457*f439e3a4SAlexander Motin 	}
458*f439e3a4SAlexander Motin }
459*f439e3a4SAlexander Motin 
460bb0ec6b3SJim Harris struct nvme_consumer *
461038a5ee4SJim Harris nvme_register_consumer(nvme_cons_ns_fn_t ns_fn, nvme_cons_ctrlr_fn_t ctrlr_fn,
462232e2edbSJim Harris 		       nvme_cons_async_fn_t async_fn,
463232e2edbSJim Harris 		       nvme_cons_fail_fn_t fail_fn)
464bb0ec6b3SJim Harris {
465bb0ec6b3SJim Harris 	int i;
466bb0ec6b3SJim Harris 
467bb0ec6b3SJim Harris 	/*
468bb0ec6b3SJim Harris 	 * TODO: add locking around consumer registration.  Not an issue
469bb0ec6b3SJim Harris 	 *  right now since we only have one nvme consumer - nvd(4).
470bb0ec6b3SJim Harris 	 */
471bb0ec6b3SJim Harris 	for (i = 0; i < NVME_MAX_CONSUMERS; i++)
472038a5ee4SJim Harris 		if (nvme_consumer[i].id == INVALID_CONSUMER_ID) {
473038a5ee4SJim Harris 			nvme_consumer[i].id = i;
474038a5ee4SJim Harris 			nvme_consumer[i].ns_fn = ns_fn;
475038a5ee4SJim Harris 			nvme_consumer[i].ctrlr_fn = ctrlr_fn;
476038a5ee4SJim Harris 			nvme_consumer[i].async_fn = async_fn;
477232e2edbSJim Harris 			nvme_consumer[i].fail_fn = fail_fn;
478bb0ec6b3SJim Harris 
479496a2752SJim Harris 			nvme_notify_new_consumer(&nvme_consumer[i]);
480bb0ec6b3SJim Harris 			return (&nvme_consumer[i]);
481bb0ec6b3SJim Harris 		}
482bb0ec6b3SJim Harris 
483bb0ec6b3SJim Harris 	printf("nvme(4): consumer not registered - no slots available\n");
484bb0ec6b3SJim Harris 	return (NULL);
485bb0ec6b3SJim Harris }
486bb0ec6b3SJim Harris 
487bb0ec6b3SJim Harris void
488bb0ec6b3SJim Harris nvme_unregister_consumer(struct nvme_consumer *consumer)
489bb0ec6b3SJim Harris {
490bb0ec6b3SJim Harris 
491038a5ee4SJim Harris 	consumer->id = INVALID_CONSUMER_ID;
492bb0ec6b3SJim Harris }
493bb0ec6b3SJim Harris 
494955910a9SJim Harris void
495955910a9SJim Harris nvme_completion_poll_cb(void *arg, const struct nvme_completion *cpl)
496955910a9SJim Harris {
497955910a9SJim Harris 	struct nvme_completion_poll_status	*status = arg;
498955910a9SJim Harris 
499955910a9SJim Harris 	/*
500955910a9SJim Harris 	 * Copy status into the argument passed by the caller, so that
501955910a9SJim Harris 	 *  the caller can check the status to determine if the
502955910a9SJim Harris 	 *  the request passed or failed.
503955910a9SJim Harris 	 */
504955910a9SJim Harris 	memcpy(&status->cpl, cpl, sizeof(*cpl));
50529077eb4SWarner Losh 	atomic_store_rel_int(&status->done, 1);
506955910a9SJim Harris }
507