Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_nvme.c
| Show First 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | |||||
| #include <machine/atomic.h> | #include <machine/atomic.h> | ||||
| #include <machine/vmm.h> | #include <machine/vmm.h> | ||||
| #include <vmmapi.h> | #include <vmmapi.h> | ||||
| #include <dev/nvme/nvme.h> | #include <dev/nvme/nvme.h> | ||||
| #include "bhyverun.h" | #include "bhyverun.h" | ||||
| #include "block_if.h" | #include "block_if.h" | ||||
| #include "config.h" | |||||
| #include "debug.h" | #include "debug.h" | ||||
| #include "pci_emul.h" | #include "pci_emul.h" | ||||
| static int nvme_debug = 0; | static int nvme_debug = 0; | ||||
| #define DPRINTF(fmt, args...) if (nvme_debug) PRINTLN(fmt, ##args) | #define DPRINTF(fmt, args...) if (nvme_debug) PRINTLN(fmt, ##args) | ||||
| #define WPRINTF(fmt, args...) PRINTLN(fmt, ##args) | #define WPRINTF(fmt, args...) PRINTLN(fmt, ##args) | ||||
| ▲ Show 20 Lines • Show All 481 Lines • ▼ Show 20 Lines | pci_nvme_init_nsdata(struct pci_nvme_softc *sc, | ||||
| nd->nlbaf = 0; /* NLBAF is a 0's based value (i.e. 1 LBA Format) */ | nd->nlbaf = 0; /* NLBAF is a 0's based value (i.e. 1 LBA Format) */ | ||||
| nd->flbas = 0; | nd->flbas = 0; | ||||
| /* Create an EUI-64 if user did not provide one */ | /* Create an EUI-64 if user did not provide one */ | ||||
| if (nvstore->eui64 == 0) { | if (nvstore->eui64 == 0) { | ||||
| char *data = NULL; | char *data = NULL; | ||||
| uint64_t eui64 = nvstore->eui64; | uint64_t eui64 = nvstore->eui64; | ||||
| asprintf(&data, "%s%u%u%u", vmname, sc->nsc_pi->pi_bus, | asprintf(&data, "%s%u%u%u", get_config_value("name"), | ||||
| sc->nsc_pi->pi_slot, sc->nsc_pi->pi_func); | sc->nsc_pi->pi_bus, sc->nsc_pi->pi_slot, | ||||
| sc->nsc_pi->pi_func); | |||||
| if (data != NULL) { | if (data != NULL) { | ||||
| eui64 = OUI_FREEBSD_NVME_LOW | crc16(0, data, strlen(data)); | eui64 = OUI_FREEBSD_NVME_LOW | crc16(0, data, strlen(data)); | ||||
| free(data); | free(data); | ||||
| } | } | ||||
| nvstore->eui64 = (eui64 << 16) | (nsid & 0xffff); | nvstore->eui64 = (eui64 << 16) | (nsid & 0xffff); | ||||
| } | } | ||||
| be64enc(nd->eui64, nvstore->eui64); | be64enc(nd->eui64, nvstore->eui64); | ||||
| ▲ Show 20 Lines • Show All 2,013 Lines • ▼ Show 20 Lines | pci_nvme_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, | ||||
| default: | default: | ||||
| DPRINTF("unknown bar %d, 0x%lx", baridx, offset); | DPRINTF("unknown bar %d, 0x%lx", baridx, offset); | ||||
| } | } | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static int | static int | ||||
| pci_nvme_parse_opts(struct pci_nvme_softc *sc, char *opts) | pci_nvme_parse_config(struct pci_nvme_softc *sc, nvlist_t *nvl) | ||||
| { | { | ||||
| char bident[sizeof("XX:X:X")]; | char bident[sizeof("XX:X:X")]; | ||||
| char *uopt, *xopts, *config; | const char *value; | ||||
| uint32_t sectsz; | uint32_t sectsz; | ||||
| int optidx; | |||||
| sc->max_queues = NVME_QUEUES; | sc->max_queues = NVME_QUEUES; | ||||
| sc->max_qentries = NVME_MAX_QENTRIES; | sc->max_qentries = NVME_MAX_QENTRIES; | ||||
| sc->ioslots = NVME_IOSLOTS; | sc->ioslots = NVME_IOSLOTS; | ||||
| sc->num_squeues = sc->max_queues; | sc->num_squeues = sc->max_queues; | ||||
| sc->num_cqueues = sc->max_queues; | sc->num_cqueues = sc->max_queues; | ||||
| sc->dataset_management = NVME_DATASET_MANAGEMENT_AUTO; | sc->dataset_management = NVME_DATASET_MANAGEMENT_AUTO; | ||||
| sectsz = 0; | sectsz = 0; | ||||
| uopt = strdup(opts); | |||||
| optidx = 0; | |||||
| snprintf(sc->ctrldata.sn, sizeof(sc->ctrldata.sn), | snprintf(sc->ctrldata.sn, sizeof(sc->ctrldata.sn), | ||||
| "NVME-%d-%d", sc->nsc_pi->pi_slot, sc->nsc_pi->pi_func); | "NVME-%d-%d", sc->nsc_pi->pi_slot, sc->nsc_pi->pi_func); | ||||
| for (xopts = strtok(uopt, ","); | |||||
| xopts != NULL; | |||||
| xopts = strtok(NULL, ",")) { | |||||
| if ((config = strchr(xopts, '=')) != NULL) | value = get_config_value_node(nvl, "maxq"); | ||||
| *config++ = '\0'; | if (value != NULL) | ||||
| sc->max_queues = atoi(value); | |||||
| if (!strcmp("maxq", xopts)) { | value = get_config_value_node(nvl, "qsz"); | ||||
| sc->max_queues = atoi(config); | if (value != NULL) { | ||||
| } else if (!strcmp("qsz", xopts)) { | sc->max_qentries = atoi(value); | ||||
| sc->max_qentries = atoi(config); | if (sc->max_qentries <= 0) { | ||||
| } else if (!strcmp("ioslots", xopts)) { | EPRINTLN("nvme: Invalid qsz option %d", | ||||
| sc->ioslots = atoi(config); | sc->max_qentries); | ||||
| } else if (!strcmp("sectsz", xopts)) { | return (-1); | ||||
| sectsz = atoi(config); | } | ||||
| } else if (!strcmp("ser", xopts)) { | } | ||||
| value = get_config_value_node(nvl, "ioslots"); | |||||
| if (value != NULL) { | |||||
| sc->ioslots = atoi(value); | |||||
| if (sc->ioslots <= 0) { | |||||
| EPRINTLN("Invalid ioslots option %d", sc->ioslots); | |||||
| return (-1); | |||||
| } | |||||
| } | |||||
| value = get_config_value_node(nvl, "sectsz"); | |||||
| if (value != NULL) | |||||
| sectsz = atoi(value); | |||||
| value = get_config_value_node(nvl, "ser"); | |||||
| if (value != NULL) { | |||||
| /* | /* | ||||
| * This field indicates the Product Serial Number in | * This field indicates the Product Serial Number in | ||||
| * 7-bit ASCII, unused bytes should be space characters. | * 7-bit ASCII, unused bytes should be space characters. | ||||
| * Ref: NVMe v1.3c. | * Ref: NVMe v1.3c. | ||||
| */ | */ | ||||
| cpywithpad((char *)sc->ctrldata.sn, | cpywithpad((char *)sc->ctrldata.sn, | ||||
| sizeof(sc->ctrldata.sn), config, ' '); | sizeof(sc->ctrldata.sn), value, ' '); | ||||
| } else if (!strcmp("ram", xopts)) { | } | ||||
| uint64_t sz = strtoull(&xopts[4], NULL, 10); | value = get_config_value_node(nvl, "eui64"); | ||||
| if (value != NULL) | |||||
| sc->nvstore.eui64 = htobe64(strtoull(value, NULL, 0)); | |||||
| value = get_config_value_node(nvl, "dsm"); | |||||
| if (value != NULL) { | |||||
| if (strcmp(value, "auto") == 0) | |||||
| sc->dataset_management = NVME_DATASET_MANAGEMENT_AUTO; | |||||
| else if (strcmp(value, "enable") == 0) | |||||
| sc->dataset_management = NVME_DATASET_MANAGEMENT_ENABLE; | |||||
| else if (strcmp(value, "disable") == 0) | |||||
| sc->dataset_management = NVME_DATASET_MANAGEMENT_DISABLE; | |||||
| } | |||||
| value = get_config_value_node(nvl, "ram"); | |||||
| if (value != NULL) { | |||||
| uint64_t sz = strtoull(value, NULL, 10); | |||||
| sc->nvstore.type = NVME_STOR_RAM; | sc->nvstore.type = NVME_STOR_RAM; | ||||
| sc->nvstore.size = sz * 1024 * 1024; | sc->nvstore.size = sz * 1024 * 1024; | ||||
| sc->nvstore.ctx = calloc(1, sc->nvstore.size); | sc->nvstore.ctx = calloc(1, sc->nvstore.size); | ||||
| sc->nvstore.sectsz = 4096; | sc->nvstore.sectsz = 4096; | ||||
| sc->nvstore.sectsz_bits = 12; | sc->nvstore.sectsz_bits = 12; | ||||
| if (sc->nvstore.ctx == NULL) { | if (sc->nvstore.ctx == NULL) { | ||||
| perror("Unable to allocate RAM"); | EPRINTLN("nvme: Unable to allocate RAM"); | ||||
| free(uopt); | |||||
| return (-1); | return (-1); | ||||
| } | } | ||||
| } else if (!strcmp("eui64", xopts)) { | } else { | ||||
| sc->nvstore.eui64 = htobe64(strtoull(config, NULL, 0)); | |||||
| } else if (!strcmp("dsm", xopts)) { | |||||
| if (!strcmp("auto", config)) | |||||
| sc->dataset_management = NVME_DATASET_MANAGEMENT_AUTO; | |||||
| else if (!strcmp("enable", config)) | |||||
| sc->dataset_management = NVME_DATASET_MANAGEMENT_ENABLE; | |||||
| else if (!strcmp("disable", config)) | |||||
| sc->dataset_management = NVME_DATASET_MANAGEMENT_DISABLE; | |||||
| } else if (optidx == 0) { | |||||
| snprintf(bident, sizeof(bident), "%d:%d", | snprintf(bident, sizeof(bident), "%d:%d", | ||||
| sc->nsc_pi->pi_slot, sc->nsc_pi->pi_func); | sc->nsc_pi->pi_slot, sc->nsc_pi->pi_func); | ||||
| sc->nvstore.ctx = blockif_open(xopts, bident); | sc->nvstore.ctx = blockif_open(nvl, bident); | ||||
| if (sc->nvstore.ctx == NULL) { | if (sc->nvstore.ctx == NULL) { | ||||
| perror("Could not open backing file"); | EPRINTLN("nvme: Could not open backing file: %s", | ||||
| free(uopt); | strerror(errno)); | ||||
| return (-1); | return (-1); | ||||
| } | } | ||||
| sc->nvstore.type = NVME_STOR_BLOCKIF; | sc->nvstore.type = NVME_STOR_BLOCKIF; | ||||
| sc->nvstore.size = blockif_size(sc->nvstore.ctx); | sc->nvstore.size = blockif_size(sc->nvstore.ctx); | ||||
| } else { | |||||
| EPRINTLN("Invalid option %s", xopts); | |||||
| free(uopt); | |||||
| return (-1); | |||||
| } | } | ||||
| optidx++; | |||||
| } | |||||
| free(uopt); | |||||
| if (sc->nvstore.ctx == NULL || sc->nvstore.size == 0) { | |||||
| EPRINTLN("backing store not specified"); | |||||
| return (-1); | |||||
| } | |||||
| if (sectsz == 512 || sectsz == 4096 || sectsz == 8192) | if (sectsz == 512 || sectsz == 4096 || sectsz == 8192) | ||||
| sc->nvstore.sectsz = sectsz; | sc->nvstore.sectsz = sectsz; | ||||
| else if (sc->nvstore.type != NVME_STOR_RAM) | else if (sc->nvstore.type != NVME_STOR_RAM) | ||||
| sc->nvstore.sectsz = blockif_sectsz(sc->nvstore.ctx); | sc->nvstore.sectsz = blockif_sectsz(sc->nvstore.ctx); | ||||
| for (sc->nvstore.sectsz_bits = 9; | for (sc->nvstore.sectsz_bits = 9; | ||||
| (1 << sc->nvstore.sectsz_bits) < sc->nvstore.sectsz; | (1 << sc->nvstore.sectsz_bits) < sc->nvstore.sectsz; | ||||
| sc->nvstore.sectsz_bits++); | sc->nvstore.sectsz_bits++); | ||||
| if (sc->max_queues <= 0 || sc->max_queues > NVME_QUEUES) | if (sc->max_queues <= 0 || sc->max_queues > NVME_QUEUES) | ||||
| sc->max_queues = NVME_QUEUES; | sc->max_queues = NVME_QUEUES; | ||||
| if (sc->max_qentries <= 0) { | |||||
| EPRINTLN("Invalid qsz option"); | |||||
| return (-1); | |||||
| } | |||||
| if (sc->ioslots <= 0) { | |||||
| EPRINTLN("Invalid ioslots option"); | |||||
| return (-1); | |||||
| } | |||||
| return (0); | return (0); | ||||
| } | } | ||||
| static int | static int | ||||
| pci_nvme_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) | pci_nvme_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl) | ||||
| { | { | ||||
| struct pci_nvme_softc *sc; | struct pci_nvme_softc *sc; | ||||
| uint32_t pci_membar_sz; | uint32_t pci_membar_sz; | ||||
| int error; | int error; | ||||
| error = 0; | error = 0; | ||||
| sc = calloc(1, sizeof(struct pci_nvme_softc)); | sc = calloc(1, sizeof(struct pci_nvme_softc)); | ||||
| pi->pi_arg = sc; | pi->pi_arg = sc; | ||||
| sc->nsc_pi = pi; | sc->nsc_pi = pi; | ||||
| error = pci_nvme_parse_opts(sc, opts); | error = pci_nvme_parse_config(sc, nvl); | ||||
| if (error < 0) | if (error < 0) | ||||
| goto done; | goto done; | ||||
| else | else | ||||
| error = 0; | error = 0; | ||||
| STAILQ_INIT(&sc->ioreqs_free); | STAILQ_INIT(&sc->ioreqs_free); | ||||
| sc->ioreqs = calloc(sc->ioslots, sizeof(struct pci_nvme_ioreq)); | sc->ioreqs = calloc(sc->ioslots, sizeof(struct pci_nvme_ioreq)); | ||||
| for (int i = 0; i < sc->ioslots; i++) { | for (int i = 0; i < sc->ioslots; i++) { | ||||
| ▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | |||||
| done: | done: | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| struct pci_devemu pci_de_nvme = { | struct pci_devemu pci_de_nvme = { | ||||
| .pe_emu = "nvme", | .pe_emu = "nvme", | ||||
| .pe_init = pci_nvme_init, | .pe_init = pci_nvme_init, | ||||
| .pe_legacy_config = blockif_legacy_config, | |||||
| .pe_barwrite = pci_nvme_write, | .pe_barwrite = pci_nvme_write, | ||||
| .pe_barread = pci_nvme_read | .pe_barread = pci_nvme_read | ||||
| }; | }; | ||||
| PCI_EMUL_SET(pci_de_nvme); | PCI_EMUL_SET(pci_de_nvme); | ||||