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); |