diff --git a/include/spdk/nvme_spec.h b/include/spdk/nvme_spec.h index d040b5b65..d760eda1c 100644 --- a/include/spdk/nvme_spec.h +++ b/include/spdk/nvme_spec.h @@ -464,6 +464,16 @@ enum spdk_nvme_cc_ams { SPDK_NVME_CC_AMS_VS = 0x7, /**< vendor specific */ }; +/** + * Fused Operation + */ +enum spdk_nvme_cmd_fuse { + SPDK_NVMF_CMD_FUSE_NONE = 0x0, /**< normal operation */ + SPDK_NVME_CMD_FUSE_FIRST = 0x1, /**< fused operation, first command */ + SPDK_NVME_CMD_FUSE_SECOND = 0x2, /**< fused operation, second command */ + /* 0x3 - reserved */ +}; + /** * Data used by Set Features/Get Features \ref SPDK_NVME_FEAT_ARBITRATION */ @@ -2821,6 +2831,10 @@ SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_fw_commit) == 4, "Incorrect size"); (cpl)->status.sc == SPDK_NVME_SC_APPLICATION_TAG_CHECK_ERROR || \ (cpl)->status.sc == SPDK_NVME_SC_REFERENCE_TAG_CHECK_ERROR)) +/** Set fused operation */ +#define SPDK_NVME_IO_FLAGS_FUSE_FIRST (1U << 0) +#define SPDK_NVME_IO_FLAGS_FUSE_SECOND (1U << 1) +#define SPDK_NVME_IO_FLAGS_FUSE_MASK (3U << 0) /** Enable protection information checking of the Logical Block Reference Tag field */ #define SPDK_NVME_IO_FLAGS_PRCHK_REFTAG (1U << 26) /** Enable protection information checking of the Application Tag field */ @@ -2833,7 +2847,8 @@ SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_fw_commit) == 4, "Incorrect size"); #define SPDK_NVME_IO_FLAGS_LIMITED_RETRY (1U << 31) /** Mask of valid io flags mask */ -#define SPDK_NVME_IO_FLAGS_VALID_MASK 0xFFFF0000 +#define SPDK_NVME_IO_FLAGS_VALID_MASK 0xFFFF0003 +#define SPDK_NVME_IO_FLAGS_CDW12_MASK 0xFFFF0000 #ifdef __cplusplus } diff --git a/lib/nvme/nvme_ns_cmd.c b/lib/nvme/nvme_ns_cmd.c index a059c0c9b..3724d6621 100644 --- a/lib/nvme/nvme_ns_cmd.c +++ b/lib/nvme/nvme_ns_cmd.c @@ -165,8 +165,10 @@ _nvme_ns_cmd_setup_request(struct spdk_nvme_ns *ns, struct nvme_request *req, } } + cmd->fuse = (io_flags & SPDK_NVME_IO_FLAGS_FUSE_MASK); + cmd->cdw12 = lba_count - 1; - cmd->cdw12 |= io_flags; + cmd->cdw12 |= (io_flags & SPDK_NVME_IO_FLAGS_CDW12_MASK); cmd->cdw15 = apptag_mask; cmd->cdw15 = (cmd->cdw15 << 16 | apptag); @@ -828,7 +830,8 @@ spdk_nvme_ns_cmd_write_zeroes(struct spdk_nvme_ns *ns, struct spdk_nvme_qpair *q tmp_lba = (uint64_t *)&cmd->cdw10; *tmp_lba = lba; cmd->cdw12 = lba_count - 1; - cmd->cdw12 |= io_flags; + cmd->fuse = (io_flags & SPDK_NVME_IO_FLAGS_FUSE_MASK); + cmd->cdw12 |= (io_flags & SPDK_NVME_IO_FLAGS_CDW12_MASK); return nvme_qpair_submit_request(qpair, req); } 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 c5ba02c82..c81d42be1 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 @@ -746,6 +746,53 @@ test_nvme_ns_cmd_comparev(void) cleanup_after_test(&qpair); } +static void +test_nvme_ns_cmd_compare_and_write(void) +{ + struct spdk_nvme_ns ns; + struct spdk_nvme_ctrlr ctrlr; + struct spdk_nvme_qpair qpair; + int rc = 0; + uint64_t lba = 0x1000; + uint32_t lba_count = 256; + uint64_t cmd_lba; + uint32_t cmd_lba_count; + uint32_t sector_size = 512; + + prepare_for_test(&ns, &ctrlr, &qpair, sector_size, 0, 128 * 1024, 0, false); + + rc = spdk_nvme_ns_cmd_compare(&ns, &qpair, NULL, lba, lba_count, NULL, NULL, + SPDK_NVME_IO_FLAGS_FUSE_FIRST); + + SPDK_CU_ASSERT_FATAL(rc == 0); + SPDK_CU_ASSERT_FATAL(g_request != NULL); + CU_ASSERT(g_request->cmd.opc == SPDK_NVME_OPC_COMPARE); + CU_ASSERT(g_request->cmd.fuse == SPDK_NVME_CMD_FUSE_FIRST); + 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, lba); + CU_ASSERT_EQUAL(cmd_lba_count, lba_count); + + nvme_free_request(g_request); + + rc = spdk_nvme_ns_cmd_write(&ns, &qpair, NULL, lba, lba_count, NULL, NULL, + SPDK_NVME_IO_FLAGS_FUSE_SECOND); + + SPDK_CU_ASSERT_FATAL(rc == 0); + SPDK_CU_ASSERT_FATAL(g_request != NULL); + CU_ASSERT(g_request->cmd.opc == SPDK_NVME_OPC_WRITE); + CU_ASSERT(g_request->cmd.fuse == SPDK_NVME_CMD_FUSE_SECOND); + 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, lba); + CU_ASSERT_EQUAL(cmd_lba_count, lba_count); + + nvme_free_request(g_request); + + cleanup_after_test(&qpair); +} + static void test_io_flags(void) { @@ -755,6 +802,8 @@ test_io_flags(void) void *payload; uint64_t lba; uint32_t lba_count; + uint64_t cmd_lba; + uint32_t cmd_lba_count; int rc; prepare_for_test(&ns, &ctrlr, &qpair, 512, 0, 128 * 1024, 128 * 1024, false); @@ -778,6 +827,21 @@ test_io_flags(void) CU_ASSERT((g_request->cmd.cdw12 & SPDK_NVME_IO_FLAGS_LIMITED_RETRY) != 0); nvme_free_request(g_request); + rc = spdk_nvme_ns_cmd_write(&ns, &qpair, payload, lba, lba_count, NULL, NULL, + SPDK_NVME_IO_FLAGS_VALID_MASK); + SPDK_CU_ASSERT_FATAL(rc == 0); + SPDK_CU_ASSERT_FATAL(g_request != NULL); + nvme_cmd_interpret_rw(&g_request->cmd, &cmd_lba, &cmd_lba_count); + CU_ASSERT_EQUAL(cmd_lba_count, lba_count); + CU_ASSERT_EQUAL(cmd_lba, lba); + CU_ASSERT_EQUAL(g_request->cmd.cdw12 & SPDK_NVME_IO_FLAGS_CDW12_MASK, + SPDK_NVME_IO_FLAGS_CDW12_MASK); + nvme_free_request(g_request); + + rc = spdk_nvme_ns_cmd_write(&ns, &qpair, payload, lba, lba_count, NULL, NULL, + ~SPDK_NVME_IO_FLAGS_VALID_MASK); + CU_ASSERT(rc == -EINVAL); + free(payload); cleanup_after_test(&qpair); } @@ -1447,6 +1511,7 @@ int main(int argc, char **argv) || CU_add_test(suite, "nvme_ns_cmd_writev", test_nvme_ns_cmd_writev) == NULL || CU_add_test(suite, "nvme_ns_cmd_write_with_md", test_nvme_ns_cmd_write_with_md) == NULL || CU_add_test(suite, "nvme_ns_cmd_comparev", test_nvme_ns_cmd_comparev) == NULL + || CU_add_test(suite, "nvme_ns_cmd_compare_and_write", test_nvme_ns_cmd_compare_and_write) == NULL || CU_add_test(suite, "nvme_ns_cmd_compare_with_md", test_nvme_ns_cmd_compare_with_md) == NULL ) { CU_cleanup_registry();