diff --git a/include/spdk/nvme.h b/include/spdk/nvme.h index 1f821a06b4..401b1c6a86 100644 --- a/include/spdk/nvme.h +++ b/include/spdk/nvme.h @@ -116,6 +116,15 @@ uint32_t nvme_ctrlr_get_num_ns(struct nvme_controller *ctrlr); */ bool nvme_ctrlr_is_log_page_supported(struct nvme_controller *ctrlr, uint8_t log_page); +/** + * \brief Determine if a particular feature is supported by the given NVMe controller. + * + * This function is thread safe and can be called at any point after nvme_attach(). + * + * \sa nvme_ctrlr_cmd_get_feature() + */ +bool nvme_ctrlr_is_feature_supported(struct nvme_controller *ctrlr, uint8_t feature_code); + /** * Signature for callback function invoked when a command is completed. * diff --git a/include/spdk/nvme_spec.h b/include/spdk/nvme_spec.h index 80bbe0aae6..37f59bea02 100644 --- a/include/spdk/nvme_spec.h +++ b/include/spdk/nvme_spec.h @@ -449,6 +449,8 @@ enum nvme_feature { NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION = 0x09, NVME_FEAT_WRITE_ATOMICITY = 0x0A, NVME_FEAT_ASYNC_EVENT_CONFIGURATION = 0x0B, + NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION = 0x0C, + NVME_FEAT_HOST_MEM_BUFFER = 0x0D, /* 0x0C-0x7F - reserved */ NVME_FEAT_SOFTWARE_PROGRESS_MARKER = 0x80, /* 0x81-0xBF - command set specific */ diff --git a/lib/nvme/nvme_ctrlr.c b/lib/nvme/nvme_ctrlr.c index 52bb1ca472..c72735746f 100644 --- a/lib/nvme/nvme_ctrlr.c +++ b/lib/nvme/nvme_ctrlr.c @@ -113,6 +113,47 @@ nvme_ctrlr_set_supported_log_pages(struct nvme_controller *ctrlr) } } +static void +nvme_ctrlr_set_intel_supported_features(struct nvme_controller *ctrlr) +{ + ctrlr->feature_supported[NVME_INTEL_FEAT_MAX_LBA] = true; + ctrlr->feature_supported[NVME_INTEL_FEAT_NATIVE_MAX_LBA] = true; + ctrlr->feature_supported[NVME_INTEL_FEAT_POWER_GOVERNOR_SETTING] = true; + ctrlr->feature_supported[NVME_INTEL_FEAT_SMBUS_ADDRESS] = true; + ctrlr->feature_supported[NVME_INTEL_FEAT_LED_PATTERN] = true; + ctrlr->feature_supported[NVME_INTEL_FEAT_RESET_TIMED_WORKLOAD_COUNTERS] = true; + ctrlr->feature_supported[NVME_INTEL_FEAT_LATENCY_TRACKING] = true; +} + +static void +nvme_ctrlr_set_supported_features(struct nvme_controller *ctrlr) +{ + memset(ctrlr->feature_supported, 0, sizeof(ctrlr->feature_supported)); + /* Mandatory features */ + ctrlr->feature_supported[NVME_FEAT_ARBITRATION] = true; + ctrlr->feature_supported[NVME_FEAT_POWER_MANAGEMENT] = true; + ctrlr->feature_supported[NVME_FEAT_TEMPERATURE_THRESHOLD] = true; + ctrlr->feature_supported[NVME_FEAT_ERROR_RECOVERY] = true; + ctrlr->feature_supported[NVME_FEAT_NUMBER_OF_QUEUES] = true; + ctrlr->feature_supported[NVME_FEAT_INTERRUPT_COALESCING] = true; + ctrlr->feature_supported[NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION] = true; + ctrlr->feature_supported[NVME_FEAT_WRITE_ATOMICITY] = true; + ctrlr->feature_supported[NVME_FEAT_ASYNC_EVENT_CONFIGURATION] = true; + /* Optional features */ + if (ctrlr->cdata.vwc.present) { + ctrlr->feature_supported[NVME_FEAT_VOLATILE_WRITE_CACHE] = true; + } + if (ctrlr->cdata.apsta.supported) { + ctrlr->feature_supported[NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION] = true; + } + if (ctrlr->cdata.hmpre) { + ctrlr->feature_supported[NVME_FEAT_HOST_MEM_BUFFER] = true; + } + if (ctrlr->cdata.vid == PCI_VENDOR_ID_INTEL) { + nvme_ctrlr_set_intel_supported_features(ctrlr); + } +} + static int nvme_ctrlr_construct_admin_qpair(struct nvme_controller *ctrlr) { @@ -693,6 +734,7 @@ nvme_ctrlr_start(struct nvme_controller *ctrlr) } nvme_ctrlr_set_supported_log_pages(ctrlr); + nvme_ctrlr_set_supported_features(ctrlr); return 0; } @@ -863,3 +905,11 @@ nvme_ctrlr_is_log_page_supported(struct nvme_controller *ctrlr, uint8_t log_page SPDK_STATIC_ASSERT(sizeof(ctrlr->log_page_supported) == 256, "log_page_supported size mismatch"); return ctrlr->log_page_supported[log_page]; } + +bool +nvme_ctrlr_is_feature_supported(struct nvme_controller *ctrlr, uint8_t feature_code) +{ + /* No bounds check necessary, since feature_code is uint8_t and feature_supported has 256 entries */ + SPDK_STATIC_ASSERT(sizeof(ctrlr->feature_supported) == 256, "feature_supported size mismatch"); + return ctrlr->feature_supported[feature_code]; +} diff --git a/lib/nvme/nvme_internal.h b/lib/nvme/nvme_internal.h index 2128738638..979456c42c 100644 --- a/lib/nvme/nvme_internal.h +++ b/lib/nvme/nvme_internal.h @@ -299,6 +299,9 @@ struct nvme_controller { /** All the log pages supported */ bool log_page_supported[256]; + /** All the features supported */ + bool feature_supported[256]; + /* Opaque handle to associated PCI device. */ void *devhandle; diff --git a/test/lib/nvme/unit/nvme_ctrlr_c/nvme_ctrlr_ut.c b/test/lib/nvme/unit/nvme_ctrlr_c/nvme_ctrlr_ut.c index 016be423d4..5c31c21f0b 100644 --- a/test/lib/nvme/unit/nvme_ctrlr_c/nvme_ctrlr_ut.c +++ b/test/lib/nvme/unit/nvme_ctrlr_c/nvme_ctrlr_ut.c @@ -225,6 +225,28 @@ test_nvme_ctrlr_construct_intel_support_log_page_list(void) CU_ASSERT(res == false); } +static void +test_nvme_ctrlr_set_supported_features(void) +{ + bool res; + struct nvme_controller ctrlr = {}; + + /* set a invalid vendor id */ + ctrlr.cdata.vid = 0xFFFF; + nvme_ctrlr_set_supported_features(&ctrlr); + res = nvme_ctrlr_is_feature_supported(&ctrlr, NVME_FEAT_ARBITRATION); + CU_ASSERT(res == true); + res = nvme_ctrlr_is_feature_supported(&ctrlr, NVME_INTEL_FEAT_MAX_LBA); + CU_ASSERT(res == false); + + ctrlr.cdata.vid = PCI_VENDOR_ID_INTEL; + nvme_ctrlr_set_supported_features(&ctrlr); + res = nvme_ctrlr_is_feature_supported(&ctrlr, NVME_FEAT_ARBITRATION); + CU_ASSERT(res == true); + res = nvme_ctrlr_is_feature_supported(&ctrlr, NVME_INTEL_FEAT_MAX_LBA); + CU_ASSERT(res == true); +} + int main(int argc, char **argv) { CU_pSuite suite = NULL; @@ -244,6 +266,8 @@ int main(int argc, char **argv) CU_add_test(suite, "test nvme_ctrlr function nvme_ctrlr_fail", test_nvme_ctrlr_fail) == NULL || CU_add_test(suite, "test nvme ctrlr function nvme_ctrlr_construct_intel_support_log_page_list", test_nvme_ctrlr_construct_intel_support_log_page_list) == NULL + || CU_add_test(suite, "test nvme ctrlr function nvme_ctrlr_set_supported_features", + test_nvme_ctrlr_set_supported_features) == NULL ) { CU_cleanup_registry(); return CU_get_error();