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