Index: usr.sbin/bhyve/pci_nvme.c =================================================================== --- usr.sbin/bhyve/pci_nvme.c +++ usr.sbin/bhyve/pci_nvme.c @@ -179,6 +179,7 @@ uint32_t sectsz; uint32_t sectsz_bits; uint64_t eui64; + uint32_t deallocate:1; }; struct pci_nvme_ioreq { @@ -361,7 +362,7 @@ cd->cqes = (4 << NVME_CTRLR_DATA_CQES_MAX_SHIFT) | (4 << NVME_CTRLR_DATA_CQES_MIN_SHIFT); cd->nn = 1; /* number of namespaces */ - + cd->oncs = (NVME_CTRLR_DATA_ONCS_DSM_MASK << NVME_CTRLR_DATA_ONCS_DSM_SHIFT); cd->fna = 0x03; cd->power_state[0].mp = 10; @@ -428,6 +429,9 @@ nd->ncap = nd->nsze; nd->nuse = nd->nsze; + if (nvstore->type == NVME_STOR_BLOCKIF) + nvstore->deallocate = blockif_candelete(nvstore->ctx); + nd->nlbaf = 0; /* NLBAF is a 0's based value (i.e. 1 LBA Format) */ nd->flbas = 0; @@ -1358,6 +1362,67 @@ pthread_cond_signal(&req->cv); } +static uint16_t +nvme_opc_dataset_mgmt(struct pci_nvme_softc *sc, + struct nvme_command *cmd, + struct pci_nvme_blockstore *nvstore, + struct pci_nvme_ioreq *req) +{ + uint16_t status = 0; + + if (cmd->cdw11 & NVME_DSM_ATTR_DEALLOCATE) { + uint8_t *data; + struct nvme_dsm_range *range; + uint32_t nr, r; + int err = 0; + + /* Return error if backing store doesn't support TRIM */ + if (!nvstore->deallocate) { + pci_nvme_status_genc(&status, NVME_SC_INVALID_FIELD); + status |= NVME_STATUS_DNR_MASK << NVME_STATUS_DNR_SHIFT; + goto out; + } + + if (req == NULL) { + pci_nvme_status_genc(&status, NVME_SC_INTERNAL_DEVICE_ERROR); + goto out; + } + + req->io_req.br_iovcnt = 0; + req->io_req.br_callback = pci_nvme_io_partial; + + /* copy locally because a range entry could straddle PRPs */ + data = calloc(256, sizeof(struct nvme_dsm_range)); + if (data == NULL) { + pci_nvme_status_genc(&status, NVME_SC_INTERNAL_DEVICE_ERROR); + goto out; + } + nvme_prp_memcpy(sc->nsc_pi->pi_vmctx, cmd->prp1, cmd->prp2, + data, 4096, NVME_COPY_FROM_PRP); + + /* Number of Ranges is a zero based value */ + nr = cmd->cdw10 & 0xff; + range = (struct nvme_dsm_range *)data; + for (r = 0; r <= nr; r++) { + req->io_req.br_offset = range[r].starting_lba * + blockif_sectsz(nvstore->ctx); + req->io_req.br_resid = range[r].length * + blockif_sectsz(nvstore->ctx); + + err = blockif_delete(nvstore->ctx, &req->io_req); + if (err == 0) + pthread_cond_wait(&req->cv, &req->mtx); + else + pci_nvme_status_genc(&status, + NVME_SC_INTERNAL_DEVICE_ERROR); + } + + if (data != NULL) + free(data); + } +out: + return (status); +} static void pci_nvme_handle_io_cmd(struct pci_nvme_softc* sc, uint16_t idx) @@ -1410,16 +1475,25 @@ continue; } - nblocks = (cmd->cdw12 & 0xFFFF) + 1; - - bytes = nblocks * sc->nvstore.sectsz; - if (sc->nvstore.type == NVME_STOR_BLOCKIF) { req = pci_nvme_get_ioreq(sc); req->nvme_sq = sq; req->sqid = idx; } + if (cmd->opc == NVME_OPC_DATASET_MANAGEMENT) { + WPRINTF(("%s DSM - attribute=%#x\r\n", __func__, cmd->cdw11)); + status = nvme_opc_dataset_mgmt(sc, cmd, &sc->nvstore, + req); + pci_nvme_set_completion(sc, sq, idx, cmd->cid, 0, status, 1); + pci_nvme_release_ioreq(sc, req); + continue; + } + + nblocks = (cmd->cdw12 & 0xFFFF) + 1; + + bytes = nblocks * sc->nvstore.sectsz; + /* * If data starts mid-page and flows into the next page, then * increase page count