FeatureStore: Add adminGetFeatures and adminSetFeatures to NVMeMi Implements the low-level NVMe-MI admin commands for getting and setting features. Design-Doc: go/bmc-redfish-nvme-get-set-features Google-Bug-Id: 405609473 Change-Id: I280a58835c9250d03b883ef1a5ddd8a5ce9aa24b Signed-off-by: Guangzong Chen <guangzong@google.com>
diff --git a/src/NVMeIntf.hpp b/src/NVMeIntf.hpp index 48b55f5..772ab4c 100644 --- a/src/NVMeIntf.hpp +++ b/src/NVMeIntf.hpp
@@ -182,6 +182,32 @@ nvme_mi_ctrl_t ctrl, nvme_identify_cns cns, uint32_t nsid, uint16_t cntid, std::function<void(nvme_ex_ptr, std::span<uint8_t>)>&& cb) = 0; + struct GetFeaturesRequest + { + uint8_t fid; + uint8_t sel; + uint32_t cdw11; + uint32_t nsid; + size_t data_len; + }; + + virtual void + adminGetFeatures(nvme_mi_ctrl_t ctrl, const GetFeaturesRequest& req, + std::function<void(nvme_ex_ptr, uint32_t response, + std::span<uint8_t>)>&& cb) = 0; + + struct SetFeaturesRequest + { + uint8_t fid; + uint32_t cdw11; + uint32_t nsid; + bool save; + std::span<const uint8_t> data; + }; + + virtual void adminSetFeatures( + nvme_mi_ctrl_t ctrl, const SetFeaturesRequest& req, + std::function<void(nvme_ex_ptr, uint32_t response)>&& cb) = 0; virtual void adminGetLogPage( nvme_mi_ctrl_t ctrl, nvme_cmd_get_log_lid lid, uint32_t nsid, uint8_t lsp, uint16_t lsi,
diff --git a/src/NVMeMi.cpp b/src/NVMeMi.cpp index a58239c..195ef1c 100644 --- a/src/NVMeMi.cpp +++ b/src/NVMeMi.cpp
@@ -843,6 +843,146 @@ } } +void NVMeMi::adminGetFeatures( + nvme_mi_ctrl_t ctrl, const GetFeaturesRequest& req, + std::function<void(nvme_ex_ptr, uint32_t, std::span<uint8_t>)>&& cb) +{ + if (auto degraded = isEndpointDegraded()) + { + lg2::error("MCTP connection is not established", "DEVICE", + device->describe()); + boost::asio::post(io, [cb{std::move(cb)}]() { + cb(makeLibNVMeError("nvme endpoint is degraded"), 0, {}); + }); + return; + } + try + { + post([ctrl, fid{req.fid}, sel{req.sel}, cdw11{req.cdw11}, + nsid{req.nsid}, dataLen{req.data_len}, self{shared_from_this()}, + ep{endpoint}, cb{std::move(cb)}]() mutable { + nvme_get_features_args getArgs{}; + getArgs.args_size = sizeof(getArgs); + getArgs.fid = fid; + getArgs.sel = static_cast<nvme_get_features_sel>(sel); + getArgs.cdw11 = cdw11; + getArgs.nsid = nsid; + + std::vector<uint8_t> data; + if (dataLen > 0) + { + data.resize(dataLen); + getArgs.data = data.data(); + getArgs.data_len = dataLen; + } + + uint32_t response = 0; + getArgs.result = &response; + + int rc = nvme_mi_admin_get_features(ctrl, &getArgs); + + if (rc < 0) + { + lg2::error("fail to do nvme get features: {ERROR}", "ENDPOINT", + ep->describe(), "ERROR", strerror(errno)); + } + else if (rc > 0) + { + std::string_view errMsg = + statusToString(static_cast<nvme_mi_resp_status>(rc)); + lg2::error("fail to do nvme get features: {ERROR}", "ENDPOINT", + ep->describe(), "ERROR", errMsg.data()); + } + + auto ex = makeLibNVMeError(errno, rc, "adminGetFeatures"); + + boost::asio::post(self->io, [cb{cb}, ex, response, + data{std::move(data)}]() mutable { + std::span<uint8_t> span{data.data(), data.size()}; + cb(ex, response, span); + }); + }); + } + catch (const std::runtime_error& e) + { + lg2::error("{ERROR}", "ENDPOINT", endpoint->describe(), "ERROR", + e.what()); + auto msg = std::string("Runtime error: ") + e.what(); + boost::asio::post(io, [cb{std::move(cb)}, msg]() { + cb(makeLibNVMeError(msg), 0, {}); + }); + return; + } +} + +void NVMeMi::adminSetFeatures(nvme_mi_ctrl_t ctrl, + const SetFeaturesRequest& req, + std::function<void(nvme_ex_ptr, uint32_t)>&& cb) +{ + if (auto degraded = isEndpointDegraded()) + { + lg2::error("MCTP connection is not established", "DEVICE", + device->describe()); + boost::asio::post(io, [cb{std::move(cb)}]() { + cb(makeLibNVMeError("nvme endpoint is degraded"), 0); + }); + return; + } + try + { + std::vector<uint8_t> data(req.data.begin(), req.data.end()); + + post([ctrl, fid{req.fid}, cdw11{req.cdw11}, nsid{req.nsid}, + save{req.save}, data{std::move(data)}, self{shared_from_this()}, + ep{endpoint}, cb{std::move(cb)}]() mutable { + nvme_set_features_args setArgs{}; + setArgs.args_size = sizeof(setArgs); + setArgs.fid = fid; + setArgs.cdw11 = cdw11; + setArgs.nsid = nsid; + setArgs.save = save; + + if (!data.empty()) + { + setArgs.data = data.data(); + setArgs.data_len = data.size(); + } + + uint32_t cdw0 = 0; + setArgs.result = &cdw0; + + int rc = nvme_mi_admin_set_features(ctrl, &setArgs); + + if (rc < 0) + { + lg2::error("fail to do nvme set features: {ERROR}", "ENDPOINT", + ep->describe(), "ERROR", strerror(errno)); + } + else if (rc > 0) + { + std::string_view errMsg = + statusToString(static_cast<nvme_mi_resp_status>(rc)); + lg2::error("fail to do nvme set features: {ERROR}", "ENDPOINT", + ep->describe(), "ERROR", errMsg.data()); + } + + auto ex = makeLibNVMeError(errno, rc, "adminSetFeatures"); + + boost::asio::post(self->io, + [cb{cb}, ex, cdw0]() mutable { cb(ex, cdw0); }); + }); + } + catch (const std::runtime_error& e) + { + lg2::error("{ERROR}", "ENDPOINT", endpoint->describe(), "ERROR", + e.what()); + auto msg = std::string("Runtime error: ") + e.what(); + boost::asio::post( + io, [cb{std::move(cb)}, msg]() { cb(makeLibNVMeError(msg), 0); }); + return; + } +} + static int nvmeMiAdminGetLogTelemetryHostRae(nvme_mi_ctrl_t ctrl, bool /*rae*/, __u64 offset, __u32 len, void* log) {
diff --git a/src/NVMeMi.hpp b/src/NVMeMi.hpp index e048c2f..4efe698 100644 --- a/src/NVMeMi.hpp +++ b/src/NVMeMi.hpp
@@ -1,3 +1,5 @@ +#pragma once + #include "NVMeIntf.hpp" #include "Utils.hpp" @@ -75,6 +77,13 @@ nvme_mi_ctrl_t ctrl, nvme_identify_cns cns, uint32_t nsid, uint16_t cntid, std::function<void(nvme_ex_ptr, std::span<uint8_t>)>&& cb) override; + void + adminGetFeatures(nvme_mi_ctrl_t ctrl, const GetFeaturesRequest& req, + std::function<void(nvme_ex_ptr, uint32_t response, + std::span<uint8_t>)>&& cb) override; + void adminSetFeatures( + nvme_mi_ctrl_t ctrl, const SetFeaturesRequest& req, + std::function<void(nvme_ex_ptr, uint32_t response)>&& cb) override; void adminGetLogPage(nvme_mi_ctrl_t ctrl, nvme_cmd_get_log_lid lid, uint32_t nsid, uint8_t lsp, uint16_t lsi, std::function<void(const std::error_code&,
diff --git a/src/NVMeMiFake.hpp b/src/NVMeMiFake.hpp index 0871aa2..6b29101 100644 --- a/src/NVMeMiFake.hpp +++ b/src/NVMeMiFake.hpp
@@ -189,6 +189,26 @@ }); } + void adminGetFeatures([[maybe_unused]] nvme_mi_ctrl_t ctrl, + [[maybe_unused]] const GetFeaturesRequest& req, + std::function<void(nvme_ex_ptr, uint32_t, + std::span<uint8_t>)>&& cb) override + { + boost::asio::post(io, [cb = std::move(cb)]() mutable { + cb(makeLibNVMeError("not supported"), 0, {}); + }); + } + + void adminSetFeatures( + [[maybe_unused]] nvme_mi_ctrl_t ctrl, + [[maybe_unused]] const SetFeaturesRequest& req, + std::function<void(nvme_ex_ptr, uint32_t)>&& cb) override + { + boost::asio::post(io, [cb = std::move(cb)]() mutable { + cb(makeLibNVMeError("not supported"), 0); + }); + } + void adminGetLogPage(nvme_mi_ctrl_t /*ctrl*/, nvme_cmd_get_log_lid lid, uint32_t /*nsid*/, uint8_t lsp, uint16_t /*lsi*/, std::function<void(const std::error_code&,
diff --git a/tests/test_nvme_mi.cpp b/tests/test_nvme_mi.cpp index 77e4927..5f1f02b 100644 --- a/tests/test_nvme_mi.cpp +++ b/tests/test_nvme_mi.cpp
@@ -108,6 +108,15 @@ uint16_t cntid, std::function<void(nvme_ex_ptr, std::span<uint8_t>)>&& cb), (override)); + MOCK_METHOD(void, adminGetFeatures, + (nvme_mi_ctrl_t ctrl, const GetFeaturesRequest& req, + std::function<void(nvme_ex_ptr, uint32_t response, + std::span<uint8_t>)>&& cb), + (override)); + MOCK_METHOD(void, adminSetFeatures, + (nvme_mi_ctrl_t ctrl, const SetFeaturesRequest& req, + std::function<void(nvme_ex_ptr, uint32_t response)>&& cb), + (override)); MOCK_METHOD( void, adminGetLogPage, (nvme_mi_ctrl_t ctrl, nvme_cmd_get_log_lid lid, uint32_t nsid,