diff --git a/CHANGELOG.md b/CHANGELOG.md index ae19ceeb8..9e44c4a3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,8 @@ can support weighted round robin arbitration feature with submission queue. Added `arbitration_burst` option for arbitration feature, and added three `low/medium/high_priority_weight` options for weighted round robin arbitration. +Added `spdk_nvme_ns_cmd_write_uncorrectable`. + ### iSCSI Portals may no longer be associated with a cpumask. The scheduling of diff --git a/include/spdk/nvme.h b/include/spdk/nvme.h index 3179ab2cc..3cfa144db 100644 --- a/include/spdk/nvme.h +++ b/include/spdk/nvme.h @@ -1814,6 +1814,7 @@ enum spdk_nvme_ns_flags { SPDK_NVME_NS_EXTENDED_LBA_SUPPORTED = 0x20, /**< The extended lba format is supported, metadata is transferred as a contiguous part of the logical block that it is associated with */ + SPDK_NVME_NS_WRITE_UNCORRECTABLE_SUPPORTED = 0x40, /**< The write uncorrectable command is supported */ }; /** @@ -1987,6 +1988,27 @@ int spdk_nvme_ns_cmd_write_zeroes(struct spdk_nvme_ns *ns, struct spdk_nvme_qpai spdk_nvme_cmd_cb cb_fn, void *cb_arg, uint32_t io_flags); +/** + * Submit a write uncorrectable I/O to the specified NVMe namespace. + * + * The command is submitted to a qpair allocated by spdk_nvme_ctrlr_alloc_io_qpair(). + * The user must ensure that only one thread submits I/O on a given qpair at any + * given time. + * + * \param ns NVMe namespace to submit the write uncorrectable I/O. + * \param qpair I/O queue pair to submit the request. + * \param lba Starting LBA for this command. + * \param lba_count Length (in sectors) for the write uncorrectable operation. + * \param cb_fn Callback function to invoke when the I/O is completed. + * \param cb_arg Argument to pass to the callback function. + * + * \return 0 if successfully submitted, negated errno if an nvme_request structure + * cannot be allocated for the I/O request. + */ +int spdk_nvme_ns_cmd_write_uncorrectable(struct spdk_nvme_ns *ns, struct spdk_nvme_qpair *qpair, + uint64_t lba, uint32_t lba_count, + spdk_nvme_cmd_cb cb_fn, void *cb_arg); + /** * \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 b88bf1746..b6b6bc58b 100644 --- a/lib/nvme/nvme_ns.c +++ b/lib/nvme/nvme_ns.c @@ -91,6 +91,10 @@ nvme_ns_set_identify_data(struct spdk_nvme_ns *ns) ns->flags |= SPDK_NVME_NS_WRITE_ZEROES_SUPPORTED; } + if (ns->ctrlr->cdata.oncs.write_unc) { + ns->flags |= SPDK_NVME_NS_WRITE_UNCORRECTABLE_SUPPORTED; + } + if (nsdata->nsrescap.raw) { ns->flags |= SPDK_NVME_NS_RESERVATION_SUPPORTED; } diff --git a/lib/nvme/nvme_ns_cmd.c b/lib/nvme/nvme_ns_cmd.c index f9debf36f..7faca0be8 100644 --- a/lib/nvme/nvme_ns_cmd.c +++ b/lib/nvme/nvme_ns_cmd.c @@ -778,6 +778,35 @@ spdk_nvme_ns_cmd_write_zeroes(struct spdk_nvme_ns *ns, struct spdk_nvme_qpair *q return nvme_qpair_submit_request(qpair, req); } +int +spdk_nvme_ns_cmd_write_uncorrectable(struct spdk_nvme_ns *ns, struct spdk_nvme_qpair *qpair, + uint64_t lba, uint32_t lba_count, + spdk_nvme_cmd_cb cb_fn, void *cb_arg) +{ + struct nvme_request *req; + struct spdk_nvme_cmd *cmd; + uint64_t *tmp_lba; + + if (lba_count == 0 || lba_count > UINT16_MAX + 1) { + return -EINVAL; + } + + req = nvme_allocate_request_null(qpair, cb_fn, cb_arg); + if (req == NULL) { + return -ENOMEM; + } + + cmd = &req->cmd; + cmd->opc = SPDK_NVME_OPC_WRITE_UNCORRECTABLE; + cmd->nsid = ns->id; + + tmp_lba = (uint64_t *)&cmd->cdw10; + *tmp_lba = lba; + cmd->cdw12 = lba_count - 1; + + return nvme_qpair_submit_request(qpair, req); +} + int spdk_nvme_ns_cmd_dataset_management(struct spdk_nvme_ns *ns, struct spdk_nvme_qpair *qpair, uint32_t type, diff --git a/test/unit/lib/nvme/nvme_ns_cmd.c/nvme_ns_cmd_ut.c b/test/unit/lib/nvme/nvme_ns_cmd.c/nvme_ns_cmd_ut.c index 66125eebd..93e6ee6e9 100644 --- a/test/unit/lib/nvme/nvme_ns_cmd.c/nvme_ns_cmd_ut.c +++ b/test/unit/lib/nvme/nvme_ns_cmd.c/nvme_ns_cmd_ut.c @@ -564,6 +564,33 @@ test_nvme_ns_cmd_write_zeroes(void) cleanup_after_test(&qpair); } +static void +test_nvme_ns_cmd_write_uncorrectable(void) +{ + struct spdk_nvme_ns ns = { 0 }; + struct spdk_nvme_ctrlr ctrlr = { 0 }; + struct spdk_nvme_qpair qpair; + spdk_nvme_cmd_cb cb_fn = NULL; + void *cb_arg = NULL; + uint64_t cmd_lba; + uint32_t cmd_lba_count; + int rc; + + prepare_for_test(&ns, &ctrlr, &qpair, 512, 0, 128 * 1024, 0, false); + + rc = spdk_nvme_ns_cmd_write_uncorrectable(&ns, &qpair, 0, 2, cb_fn, cb_arg); + SPDK_CU_ASSERT_FATAL(rc == 0); + SPDK_CU_ASSERT_FATAL(g_request != NULL); + CU_ASSERT(g_request->cmd.opc == SPDK_NVME_OPC_WRITE_UNCORRECTABLE); + 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); + cleanup_after_test(&qpair); +} + static void test_nvme_ns_cmd_dataset_management(void) { @@ -1405,6 +1432,8 @@ int main(int argc, char **argv) test_nvme_ns_cmd_dataset_management) == NULL || CU_add_test(suite, "io_flags", test_io_flags) == NULL || CU_add_test(suite, "nvme_ns_cmd_write_zeroes", test_nvme_ns_cmd_write_zeroes) == NULL + || CU_add_test(suite, "nvme_ns_cmd_write_uncorrectable", + test_nvme_ns_cmd_write_uncorrectable) == NULL || CU_add_test(suite, "nvme_ns_cmd_reservation_register", test_nvme_ns_cmd_reservation_register) == NULL || CU_add_test(suite, "nvme_ns_cmd_reservation_release",