This adds support for the optional data-set-management command. Action is only done for the deallocate option.
Signed-off-by: Keith Busch <keith.bu...@intel.com> --- hw/nvme.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 46 insertions(+), 0 deletions(-) diff --git a/hw/nvme.c b/hw/nvme.c index eeff762..69136e0 100644 --- a/hw/nvme.c +++ b/hw/nvme.c @@ -378,6 +378,47 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, return ret; } +static void nvme_dsm_dealloc(NvmeNamespace *ns, uint64_t slba, uint64_t nlb) +{ + uint64_t nr; + uint64_t elba = nlb + slba; + unsigned long *addr = ns->util; + for (nr = slba; nr < elba; nr++) { + if (test_and_clear_bit(nr, addr)) { + assert(ns->id_ns.nuse > 0); + --ns->id_ns.nuse; + } + } +} + +static uint16_t nvme_dsm(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, + NvmeRequest *req) +{ + uint16_t nr = (cmd->cdw10 & 0xff) + 1; + NvmeDsmRange range[nr]; + + if (nvme_dma_prp(cmd->prp1, cmd->prp2, sizeof(range), n, (uint8_t *)range, + DMA_DIRECTION_TO_DEVICE)) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + if (cmd->cdw11 & NVME_DSMGMT_AD) { + int i; + uint64_t slba; + uint32_t nlb; + for (i = 0; i < nr; i++) { + slba = range[i].slba; + nlb = range[i].nlb; + if (slba + nlb > ns->id_ns.ncap) { + return NVME_LBA_RANGE | NVME_DNR; + } + nvme_dsm_dealloc(ns, slba, nlb); + /* TODO: send bdrv_aio_discard request */ + } + } + return NVME_SUCCESS; +} + static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) { NvmeNamespace *ns; @@ -392,6 +433,10 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) case NVME_CMD_WRITE: case NVME_CMD_READ: return nvme_rw(n, ns, cmd, req); + case NVME_CMD_DSM: + return nvme_dsm(n, ns, cmd, req); + case NVME_CMD_COMPARE: + case NVME_CMD_WRITE_UNCOR: default: return NVME_INVALID_OPCODE | NVME_DNR; } @@ -1345,6 +1390,7 @@ static int nvme_init(PCIDevice *pci_dev) id->sqes = n->max_sqes << 4 | 0x6; id->cqes = n->max_cqes << 4 | 0x4; id->nn = n->num_namespaces; + id->oncs = NVME_ONCS_DSM; id->vwc = n->vwc; snprintf((char *)id->mn, sizeof(id->mn), "QEMU NVMe Ctrl"); snprintf((char *)id->fr, sizeof(id->fr), "1.0"); -- 1.7.0.4