diff --git a/include/spdk/nvme.h b/include/spdk/nvme.h index 33dc54766..64cec00d8 100644 --- a/include/spdk/nvme.h +++ b/include/spdk/nvme.h @@ -365,6 +365,7 @@ enum nvme_namespace_flags { NVME_NS_DEALLOCATE_SUPPORTED = 0x1, /**< The deallocate command is supported */ NVME_NS_FLUSH_SUPPORTED = 0x2, /**< The flush command is supported */ NVME_NS_RESERVATION_SUPPORTED = 0x4, /**< The reservation command is supported */ + NVME_NS_WRITE_ZEROES_SUPPORTED = 0x8, /**< The write zeroes command is supported */ }; /** @@ -440,6 +441,27 @@ int nvme_ns_cmd_writev(struct nvme_namespace *ns, uint64_t lba, uint32_t lba_cou nvme_req_reset_sgl_fn_t reset_sgl_fn, nvme_req_next_sge_fn_t next_sge_fn); +/** + * \brief Submits a write zeroes I/O to the specified NVMe namespace. + * + * \param ns NVMe namespace to submit the write zeroes I/O + * \param lba starting LBA for this command + * \param lba_count length (in sectors) for the write zero operation + * \param cb_fn callback function to invoke when the I/O is completed + * \param cb_arg argument to pass to the callback function + * \param io_flags set flags, defined by the NVME_IO_FLAGS_* entries + * in spdk/nvme_spec.h, for this I/O. + * + * \return 0 if successfully submitted, ENOMEM if an nvme_request + * structure cannot be allocated for the I/O request + * + * This function is thread safe and can be called at any point after + * nvme_register_io_thread(). + */ +int nvme_ns_cmd_write_zeroes(struct nvme_namespace *ns, uint64_t lba, + uint32_t lba_count, nvme_cb_fn_t cb_fn, void *cb_arg, + uint32_t io_flags); + /** * \brief Submits a read I/O to the specified NVMe namespace. * diff --git a/lib/nvme/nvme_ns.c b/lib/nvme/nvme_ns.c index 6e2ff9083..e2e6b640d 100644 --- a/lib/nvme/nvme_ns.c +++ b/lib/nvme/nvme_ns.c @@ -126,6 +126,10 @@ nvme_ns_construct(struct nvme_namespace *ns, uint16_t id, ns->flags |= NVME_NS_FLUSH_SUPPORTED; } + if (ctrlr->cdata.oncs.write_zeroes) { + ns->flags |= NVME_NS_WRITE_ZEROES_SUPPORTED; + } + if (nsdata->nsrescap.raw) { ns->flags |= NVME_NS_RESERVATION_SUPPORTED; } diff --git a/lib/nvme/nvme_ns_cmd.c b/lib/nvme/nvme_ns_cmd.c index 0ca5f60b1..f0c00c234 100644 --- a/lib/nvme/nvme_ns_cmd.c +++ b/lib/nvme/nvme_ns_cmd.c @@ -258,6 +258,38 @@ nvme_ns_cmd_writev(struct nvme_namespace *ns, uint64_t lba, uint32_t lba_count, } } +int +nvme_ns_cmd_write_zeroes(struct nvme_namespace *ns, uint64_t lba, + uint32_t lba_count, nvme_cb_fn_t cb_fn, void *cb_arg, + uint32_t io_flags) +{ + struct nvme_request *req; + struct nvme_command *cmd; + uint64_t *tmp_lba; + + if (lba_count == 0) { + return EINVAL; + } + + req = nvme_allocate_request_null(cb_fn, cb_arg); + if (req == NULL) { + return ENOMEM; + } + + cmd = &req->cmd; + cmd->opc = NVME_OPC_WRITE_ZEROES; + cmd->nsid = ns->id; + + tmp_lba = (uint64_t *)&cmd->cdw10; + *tmp_lba = lba; + cmd->cdw12 = lba_count - 1; + cmd->cdw12 |= io_flags; + + nvme_ctrlr_submit_io_request(ns->ctrlr, req); + + return 0; +} + int nvme_ns_cmd_deallocate(struct nvme_namespace *ns, void *payload, uint16_t num_ranges, nvme_cb_fn_t cb_fn, void *cb_arg) diff --git a/test/lib/nvme/unit/nvme_ns_cmd_c/nvme_ns_cmd_ut.c b/test/lib/nvme/unit/nvme_ns_cmd_c/nvme_ns_cmd_ut.c index 7a72dc0f5..53c3fd3f7 100644 --- a/test/lib/nvme/unit/nvme_ns_cmd_c/nvme_ns_cmd_ut.c +++ b/test/lib/nvme/unit/nvme_ns_cmd_c/nvme_ns_cmd_ut.c @@ -331,6 +331,28 @@ test_nvme_ns_cmd_flush(void) nvme_free_request(g_request); } +static void +test_nvme_ns_cmd_write_zeroes(void) +{ + struct nvme_namespace ns = { 0 }; + struct nvme_controller ctrlr = { 0 }; + nvme_cb_fn_t cb_fn = NULL; + void *cb_arg = NULL; + uint64_t cmd_lba; + uint32_t cmd_lba_count; + + prepare_for_test(&ns, &ctrlr, 512, 128 * 1024, 0); + + nvme_ns_cmd_write_zeroes(&ns, 0, 2, cb_fn, cb_arg, 0); + CU_ASSERT(g_request->cmd.opc == NVME_OPC_WRITE_ZEROES); + CU_ASSERT(g_request->cmd.nsid == ns.id); + nvme_cmd_interpret_rw(&g_request->cmd, &cmd_lba, &cmd_lba_count); + CU_ASSERT_EQUAL(cmd_lba, 0); + CU_ASSERT_EQUAL(cmd_lba_count, 2); + + nvme_free_request(g_request); +} + static void test_nvme_ns_cmd_deallocate(void) { @@ -426,6 +448,7 @@ int main(int argc, char **argv) || CU_add_test(suite, "nvme_ns_cmd_flush testing", test_nvme_ns_cmd_flush) == NULL || CU_add_test(suite, "nvme_ns_cmd_deallocate testing", test_nvme_ns_cmd_deallocate) == NULL || CU_add_test(suite, "io_flags", test_io_flags) == NULL + || CU_add_test(suite, "nvme_ns_cmd_write_zeroes testing", test_nvme_ns_cmd_write_zeroes) == NULL ) { CU_cleanup_registry(); return CU_get_error();