Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_nvme.c
Show First 20 Lines • Show All 1,523 Lines • ▼ Show 20 Lines | case NVME_OPC_READ: | ||||
break; | break; | ||||
default: | default: | ||||
DPRINTF("%s: Invalid OPC 0x%02x for stats", __func__, opc); | DPRINTF("%s: Invalid OPC 0x%02x for stats", __func__, opc); | ||||
break; | break; | ||||
} | } | ||||
pthread_mutex_unlock(&sc->mtx); | pthread_mutex_unlock(&sc->mtx); | ||||
} | } | ||||
/* | |||||
* Check if the combination of Starting LBA (slba) and Number of Logical | |||||
* Blocks (nlb) exceeds the range of the underlying storage. | |||||
* | |||||
* Because NVMe specifies the SLBA in blocks as a uint64_t and blockif stores | |||||
* the capacity in bytes as a uint64_t, care must be taken to avoid integer | |||||
* overflow. | |||||
*/ | |||||
static bool | |||||
pci_nvme_out_of_range(struct pci_nvme_blockstore *nvstore, uint64_t slba, | |||||
uint32_t nlb) | |||||
{ | |||||
size_t offset, bytes; | |||||
/* Overflow check of multiplying Starting LBA by the sector size */ | |||||
if (slba >> (64 - nvstore->sectsz_bits)) | |||||
return (true); | |||||
offset = slba << nvstore->sectsz_bits; | |||||
bytes = nlb << nvstore->sectsz_bits; | |||||
/* Overflow check of Number of Logical Blocks */ | |||||
if ((nvstore->size - offset) < bytes) | |||||
return (true); | |||||
return (false); | |||||
} | |||||
static int | static int | ||||
pci_nvme_append_iov_req(struct pci_nvme_softc *sc, struct pci_nvme_ioreq *req, | pci_nvme_append_iov_req(struct pci_nvme_softc *sc, struct pci_nvme_ioreq *req, | ||||
uint64_t gpaddr, size_t size, int do_write, uint64_t lba) | uint64_t gpaddr, size_t size, int do_write, uint64_t lba) | ||||
{ | { | ||||
int iovidx; | int iovidx; | ||||
if (req == NULL) | if (req == NULL) | ||||
return (-1); | return (-1); | ||||
▲ Show 20 Lines • Show All 285 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
uint64_t lba, nblocks, bytes; | uint64_t lba, nblocks, bytes; | ||||
size_t offset; | size_t offset; | ||||
bool is_write = cmd->opc == NVME_OPC_WRITE; | bool is_write = cmd->opc == NVME_OPC_WRITE; | ||||
bool pending = false; | bool pending = false; | ||||
lba = ((uint64_t)cmd->cdw11 << 32) | cmd->cdw10; | lba = ((uint64_t)cmd->cdw11 << 32) | cmd->cdw10; | ||||
nblocks = (cmd->cdw12 & 0xFFFF) + 1; | nblocks = (cmd->cdw12 & 0xFFFF) + 1; | ||||
if (pci_nvme_out_of_range(nvstore, lba, nblocks)) { | |||||
WPRINTF("%s command would exceed LBA range", __func__); | |||||
pci_nvme_status_genc(status, NVME_SC_LBA_OUT_OF_RANGE); | |||||
goto out; | |||||
} | |||||
bytes = nblocks * nvstore->sectsz; | bytes = nblocks << nvstore->sectsz_bits; | ||||
if (bytes > NVME_MAX_DATA_SIZE) { | if (bytes > NVME_MAX_DATA_SIZE) { | ||||
WPRINTF("%s command would exceed MDTS", __func__); | WPRINTF("%s command would exceed MDTS", __func__); | ||||
pci_nvme_status_genc(status, NVME_SC_INVALID_FIELD); | pci_nvme_status_genc(status, NVME_SC_INVALID_FIELD); | ||||
goto out; | goto out; | ||||
} | } | ||||
offset = lba * nvstore->sectsz; | offset = lba << nvstore->sectsz_bits; | ||||
if ((offset + bytes) > nvstore->size) { | |||||
WPRINTF("%s command would exceed LBA range", __func__); | |||||
pci_nvme_status_genc(status, NVME_SC_LBA_OUT_OF_RANGE); | |||||
goto out; | |||||
} | |||||
req->bytes = bytes; | req->bytes = bytes; | ||||
req->io_req.br_offset = lba; | req->io_req.br_offset = lba; | ||||
/* PRP bits 1:0 must be zero */ | /* PRP bits 1:0 must be zero */ | ||||
cmd->prp1 &= ~0x3UL; | cmd->prp1 &= ~0x3UL; | ||||
cmd->prp2 &= ~0x3UL; | cmd->prp2 &= ~0x3UL; | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | nvme_opc_dataset_mgmt(struct pci_nvme_softc *sc, | ||||
if ((sc->ctrldata.oncs & NVME_ONCS_DSM) == 0) { | if ((sc->ctrldata.oncs & NVME_ONCS_DSM) == 0) { | ||||
pci_nvme_status_genc(status, NVME_SC_INVALID_OPCODE); | pci_nvme_status_genc(status, NVME_SC_INVALID_OPCODE); | ||||
goto out; | goto out; | ||||
} | } | ||||
if (cmd->cdw11 & NVME_DSM_ATTR_DEALLOCATE) { | if (cmd->cdw11 & NVME_DSM_ATTR_DEALLOCATE) { | ||||
struct nvme_dsm_range *range; | struct nvme_dsm_range *range; | ||||
size_t offset, bytes; | |||||
uint32_t nr, r; | uint32_t nr, r; | ||||
int sectsz = sc->nvstore.sectsz; | int sectsz_bits = sc->nvstore.sectsz_bits; | ||||
/* | /* | ||||
* DSM calls are advisory only, and compliant controllers | * DSM calls are advisory only, and compliant controllers | ||||
* may choose to take no actions (i.e. return Success). | * may choose to take no actions (i.e. return Success). | ||||
*/ | */ | ||||
if (!nvstore->deallocate) { | if (!nvstore->deallocate) { | ||||
pci_nvme_status_genc(status, NVME_SC_SUCCESS); | pci_nvme_status_genc(status, NVME_SC_SUCCESS); | ||||
goto out; | goto out; | ||||
} | } | ||||
if (req == NULL) { | if (req == NULL) { | ||||
pci_nvme_status_genc(status, NVME_SC_INTERNAL_DEVICE_ERROR); | pci_nvme_status_genc(status, NVME_SC_INTERNAL_DEVICE_ERROR); | ||||
goto out; | goto out; | ||||
} | } | ||||
/* copy locally because a range entry could straddle PRPs */ | /* copy locally because a range entry could straddle PRPs */ | ||||
range = calloc(1, NVME_MAX_DSM_TRIM); | range = calloc(1, NVME_MAX_DSM_TRIM); | ||||
if (range == NULL) { | if (range == NULL) { | ||||
pci_nvme_status_genc(status, NVME_SC_INTERNAL_DEVICE_ERROR); | pci_nvme_status_genc(status, NVME_SC_INTERNAL_DEVICE_ERROR); | ||||
goto out; | goto out; | ||||
} | } | ||||
nvme_prp_memcpy(sc->nsc_pi->pi_vmctx, cmd->prp1, cmd->prp2, | nvme_prp_memcpy(sc->nsc_pi->pi_vmctx, cmd->prp1, cmd->prp2, | ||||
(uint8_t *)range, NVME_MAX_DSM_TRIM, NVME_COPY_FROM_PRP); | (uint8_t *)range, NVME_MAX_DSM_TRIM, NVME_COPY_FROM_PRP); | ||||
if ((range[0].starting_lba * sectsz) > nvstore->size) { | if (pci_nvme_out_of_range(nvstore, range[0].starting_lba, | ||||
range[0].length)) { | |||||
pci_nvme_status_genc(status, NVME_SC_LBA_OUT_OF_RANGE); | pci_nvme_status_genc(status, NVME_SC_LBA_OUT_OF_RANGE); | ||||
goto out; | goto out; | ||||
} | } | ||||
offset = range[0].starting_lba << sectsz_bits; | |||||
bytes = range[0].length << sectsz_bits; | |||||
/* | /* | ||||
* If the request is for more than a single range, store | * If the request is for more than a single range, store | ||||
* the ranges in the br_iov. Optimize for the common case | * the ranges in the br_iov. Optimize for the common case | ||||
* of a single range. | * of a single range. | ||||
* | * | ||||
* Note that NVMe Number of Ranges is a zero based value | * Note that NVMe Number of Ranges is a zero based value | ||||
*/ | */ | ||||
nr = cmd->cdw10 & 0xff; | nr = cmd->cdw10 & 0xff; | ||||
req->io_req.br_iovcnt = 0; | req->io_req.br_iovcnt = 0; | ||||
req->io_req.br_offset = range[0].starting_lba * sectsz; | req->io_req.br_offset = offset; | ||||
req->io_req.br_resid = range[0].length * sectsz; | req->io_req.br_resid = bytes; | ||||
if (nr == 0) { | if (nr == 0) { | ||||
req->io_req.br_callback = pci_nvme_io_done; | req->io_req.br_callback = pci_nvme_io_done; | ||||
} else { | } else { | ||||
struct iovec *iov = req->io_req.br_iov; | struct iovec *iov = req->io_req.br_iov; | ||||
for (r = 0; r <= nr; r++) { | for (r = 0; r <= nr; r++) { | ||||
if ((range[r].starting_lba * sectsz) > nvstore->size) { | if (pci_nvme_out_of_range(nvstore, range[r].starting_lba, | ||||
range[r].length)) { | |||||
pci_nvme_status_genc(status, NVME_SC_LBA_OUT_OF_RANGE); | pci_nvme_status_genc(status, NVME_SC_LBA_OUT_OF_RANGE); | ||||
goto out; | goto out; | ||||
} | } | ||||
iov[r].iov_base = (void *)(range[r].starting_lba * sectsz); | offset = range[r].starting_lba << sectsz_bits; | ||||
iov[r].iov_len = range[r].length * sectsz; | bytes = range[r].length << sectsz_bits; | ||||
if ((nvstore->size - offset) < bytes) { | |||||
pci_nvme_status_genc(status, NVME_SC_LBA_OUT_OF_RANGE); | |||||
goto out; | |||||
} | |||||
iov[r].iov_base = (void *)offset; | |||||
iov[r].iov_len = bytes; | |||||
} | } | ||||
req->io_req.br_callback = pci_nvme_dealloc_sm; | req->io_req.br_callback = pci_nvme_dealloc_sm; | ||||
/* | /* | ||||
* Use prev_gpaddr to track the current entry and | * Use prev_gpaddr to track the current entry and | ||||
* prev_size to track the number of entries | * prev_size to track the number of entries | ||||
*/ | */ | ||||
req->prev_gpaddr = 0; | req->prev_gpaddr = 0; | ||||
▲ Show 20 Lines • Show All 611 Lines • Show Last 20 Lines |