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