diff --git a/include/spdk/nvme.h b/include/spdk/nvme.h index 5a731ccaa..10a633af5 100644 --- a/include/spdk/nvme.h +++ b/include/spdk/nvme.h @@ -876,6 +876,62 @@ int spdk_nvme_ctrlr_cmd_get_feature(struct spdk_nvme_ctrlr *ctrlr, void *payload, uint32_t payload_size, spdk_nvme_cmd_cb cb_fn, void *cb_arg); +/** + * \brief Get specific feature from given NVMe controller. + * + * \param ctrlr NVMe controller to query. + * \param feature The feature identifier. + * \param cdw11 as defined by the specification for this command. + * \param payload The pointer to the payload buffer. + * \param payload_size The size of payload buffer. + * \param cb_fn Callback function to invoke when the feature has been retrieved. + * \param cb_arg Argument to pass to the callback function. + * \param ns_is The namespace identifier. + * + * \return 0 if successfully submitted, ENOMEM if resources could not be allocated + * for this request + * + * This function is thread safe and can be called at any point while the controller + * is attached to the SPDK NVMe driver. + * + * Call \ref spdk_nvme_ctrlr_process_admin_completions() to poll for completion + * of commands submitted through this function. + * + * \sa spdk_nvme_ctrlr_cmd_set_feature_ns() + */ +int spdk_nvme_ctrlr_cmd_get_feature_ns(struct spdk_nvme_ctrlr *ctrlr, uint8_t feature, + uint32_t cdw11, void *payload, uint32_t payload_size, + spdk_nvme_cmd_cb cb_fn, void *cb_arg, uint32_t ns_id); + +/** + * \brief Set specific feature for the given NVMe controller and namespace ID. + * + * \param ctrlr NVMe controller to manipulate. + * \param feature The feature identifier. + * \param cdw11 as defined by the specification for this command. + * \param cdw12 as defined by the specification for this command. + * \param payload The pointer to the payload buffer. + * \param payload_size The size of payload buffer. + * \param cb_fn Callback function to invoke when the feature has been set. + * \param cb_arg Argument to pass to the callback function. + * \param ns_is The namespace identifier. + * + * \return 0 if successfully submitted, ENOMEM if resources could not be allocated + * for this request. + * + * This function is thread safe and can be called at any point while the controller + * is attached to the SPDK NVMe driver. + * + * Call \ref spdk_nvme_ctrlr_process_admin_completions() to poll for completion + * of commands submitted through this function. + * + * \sa spdk_nvme_ctrlr_cmd_get_feature_ns() + */ +int spdk_nvme_ctrlr_cmd_set_feature_ns(struct spdk_nvme_ctrlr *ctrlr, uint8_t feature, + uint32_t cdw11, uint32_t cdw12, void *payload, + uint32_t payload_size, spdk_nvme_cmd_cb cb_fn, + void *cb_arg, uint32_t ns_id); + /** * \brief Attach the specified namespace to controllers. * diff --git a/lib/nvme/nvme_ctrlr_cmd.c b/lib/nvme/nvme_ctrlr_cmd.c index 7d72c78c2..2b6b23b14 100644 --- a/lib/nvme/nvme_ctrlr_cmd.c +++ b/lib/nvme/nvme_ctrlr_cmd.c @@ -340,6 +340,66 @@ spdk_nvme_ctrlr_cmd_get_feature(struct spdk_nvme_ctrlr *ctrlr, uint8_t feature, return rc; } +int +spdk_nvme_ctrlr_cmd_get_feature_ns(struct spdk_nvme_ctrlr *ctrlr, uint8_t feature, + uint32_t cdw11, void *payload, + uint32_t payload_size, spdk_nvme_cmd_cb cb_fn, + void *cb_arg, uint32_t ns_id) +{ + struct nvme_request *req; + struct spdk_nvme_cmd *cmd; + int rc; + + nvme_robust_mutex_lock(&ctrlr->ctrlr_lock); + req = nvme_allocate_request_user_copy(ctrlr->adminq, payload, payload_size, cb_fn, cb_arg, + false); + if (req == NULL) { + nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); + return -ENOMEM; + } + + cmd = &req->cmd; + cmd->opc = SPDK_NVME_OPC_GET_FEATURES; + cmd->cdw10 = feature; + cmd->cdw11 = cdw11; + cmd->nsid = ns_id; + + rc = nvme_ctrlr_submit_admin_request(ctrlr, req); + nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); + + return rc; +} + +int spdk_nvme_ctrlr_cmd_set_feature_ns(struct spdk_nvme_ctrlr *ctrlr, uint8_t feature, + uint32_t cdw11, uint32_t cdw12, void *payload, + uint32_t payload_size, spdk_nvme_cmd_cb cb_fn, + void *cb_arg, uint32_t ns_id) +{ + struct nvme_request *req; + struct spdk_nvme_cmd *cmd; + int rc; + + nvme_robust_mutex_lock(&ctrlr->ctrlr_lock); + req = nvme_allocate_request_user_copy(ctrlr->adminq, payload, payload_size, cb_fn, cb_arg, + true); + if (req == NULL) { + nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); + return -ENOMEM; + } + + cmd = &req->cmd; + cmd->opc = SPDK_NVME_OPC_SET_FEATURES; + cmd->cdw10 = feature; + cmd->cdw11 = cdw11; + cmd->cdw12 = cdw12; + cmd->nsid = ns_id; + + rc = nvme_ctrlr_submit_admin_request(ctrlr, req); + nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); + + return rc; +} + int nvme_ctrlr_cmd_set_num_queues(struct spdk_nvme_ctrlr *ctrlr, uint32_t num_queues, spdk_nvme_cmd_cb cb_fn, void *cb_arg) diff --git a/test/unit/lib/nvme/nvme_ctrlr_cmd.c/nvme_ctrlr_cmd_ut.c b/test/unit/lib/nvme/nvme_ctrlr_cmd.c/nvme_ctrlr_cmd_ut.c index ccb7d8b4c..8cbc44761 100644 --- a/test/unit/lib/nvme/nvme_ctrlr_cmd.c/nvme_ctrlr_cmd_ut.c +++ b/test/unit/lib/nvme/nvme_ctrlr_cmd.c/nvme_ctrlr_cmd_ut.c @@ -56,6 +56,11 @@ uint16_t abort_sqid = 1; uint32_t namespace_management_nsid = 1; uint32_t format_nvme_nsid = 1; +uint32_t expected_feature_ns = 2; +uint32_t expected_feature_cdw10 = SPDK_NVME_FEAT_LBA_RANGE_TYPE; +uint32_t expected_feature_cdw11 = 1; +uint32_t expected_feature_cdw12 = 1; + typedef void (*verify_request_fn_t)(struct nvme_request *req); verify_request_fn_t verify_fn; @@ -103,6 +108,15 @@ static void verify_set_feature_cmd(struct nvme_request *req) CU_ASSERT(req->cmd.cdw12 == feature_cdw12); } +static void verify_set_feature_ns_cmd(struct nvme_request *req) +{ + CU_ASSERT(req->cmd.opc == SPDK_NVME_OPC_SET_FEATURES); + CU_ASSERT(req->cmd.cdw10 == expected_feature_cdw10); + CU_ASSERT(req->cmd.cdw11 == expected_feature_cdw11); + CU_ASSERT(req->cmd.cdw12 == expected_feature_cdw12); + CU_ASSERT(req->cmd.nsid == expected_feature_ns); +} + static void verify_get_feature_cmd(struct nvme_request *req) { CU_ASSERT(req->cmd.opc == SPDK_NVME_OPC_GET_FEATURES); @@ -110,6 +124,14 @@ static void verify_get_feature_cmd(struct nvme_request *req) CU_ASSERT(req->cmd.cdw11 == get_feature_cdw11); } +static void verify_get_feature_ns_cmd(struct nvme_request *req) +{ + CU_ASSERT(req->cmd.opc == SPDK_NVME_OPC_GET_FEATURES); + CU_ASSERT(req->cmd.cdw10 == expected_feature_cdw10); + CU_ASSERT(req->cmd.cdw11 == expected_feature_cdw11); + CU_ASSERT(req->cmd.nsid == expected_feature_ns); +} + static void verify_abort_cmd(struct nvme_request *req) { CU_ASSERT(req->cmd.opc == SPDK_NVME_OPC_ABORT); @@ -423,6 +445,29 @@ test_set_feature_cmd(void) spdk_nvme_ctrlr_cmd_set_feature(&ctrlr, feature, feature_cdw11, feature_cdw12, NULL, 0, NULL, NULL); } +static void +test_get_feature_ns_cmd(void) +{ + DECLARE_AND_CONSTRUCT_CTRLR(); + + verify_fn = verify_get_feature_ns_cmd; + + spdk_nvme_ctrlr_cmd_get_feature_ns(&ctrlr, expected_feature_cdw10, + expected_feature_cdw11, NULL, 0, + NULL, NULL, expected_feature_ns); +} + +static void +test_set_feature_ns_cmd(void) +{ + DECLARE_AND_CONSTRUCT_CTRLR(); + + verify_fn = verify_set_feature_ns_cmd; + + spdk_nvme_ctrlr_cmd_set_feature_ns(&ctrlr, expected_feature_cdw10, + expected_feature_cdw11, expected_feature_cdw12, + NULL, 0, NULL, NULL, expected_feature_ns); +} static void test_get_feature_cmd(void) @@ -574,7 +619,9 @@ int main(int argc, char **argv) if ( CU_add_test(suite, "test ctrlr cmd get_log_pages", test_get_log_pages) == NULL || CU_add_test(suite, "test ctrlr cmd set_feature", test_set_feature_cmd) == NULL + || CU_add_test(suite, "test ctrlr cmd set_feature_ns", test_set_feature_ns_cmd) == NULL || CU_add_test(suite, "test ctrlr cmd get_feature", test_get_feature_cmd) == NULL + || CU_add_test(suite, "test ctrlr cmd get_feature_ns", test_get_feature_ns_cmd) == NULL || CU_add_test(suite, "test ctrlr cmd abort_cmd", test_abort_cmd) == NULL || CU_add_test(suite, "test ctrlr cmd io_raw_cmd", test_io_raw_cmd) == NULL || CU_add_test(suite, "test ctrlr cmd io_raw_cmd_with_md", test_io_raw_cmd_with_md) == NULL