NVMeCache: Add implementation for Identify Metric Identify Metric is used to store the cached info for Identify info for NVMe controllers and namespace. Also instantiate the Identify Metric for controllers. Tested: b/348660385#comment12 Google-Bug-Id: 348660385 Signed-off-by: Hao Jiang <jianghao@google.com> Change-Id: I92f96cb63e68d9071b5e85eea3810e91616d948c
diff --git a/src/NVMeCacheImpl.hpp b/src/NVMeCacheImpl.hpp index 124197b..5f2b05a 100644 --- a/src/NVMeCacheImpl.hpp +++ b/src/NVMeCacheImpl.hpp
@@ -5,6 +5,7 @@ #include "NVMeCache.hpp" #include "NVMeController.hpp" +#include "NVMeVolume.hpp" /** * Metric implementation for LogPage @@ -112,7 +113,7 @@ if (ec) { if (self->errorCount < - ControllerLogPageMetric<LID>::errorCountMax) + ControllerLogPageMetric<LID, ClockType>::errorCountMax) { // TODO: error_level = WARN std::cerr << std::format( @@ -162,3 +163,158 @@ unsigned errorCount{0}; static constexpr unsigned errorCountMax = 5; }; + +/** + * Metric implementation for Identify info + */ +template <nvme_identify_cns CNS, class ClockType> +class IdentifyMetric : public MetricBase<ClockType> +{ + public: + template <class T> + requires((CNS == NVME_IDENTIFY_CNS_CTRL && + std::is_base_of_v<NVMeControllerEnabled, T>) || + (CNS == NVME_IDENTIFY_CNS_NS && + std::is_base_of_v<NVMeVolume, T>)) + IdentifyMetric(std::shared_ptr<T> resource, + std::shared_ptr<Scheduler<ClockType>> scheduler, + ClockType::duration interval, + std::function<void(std::shared_ptr<MetricBase<ClockType>>)>&& + completeCB) : + MetricBase<ClockType>(scheduler, interval), + completeCB(std::move(completeCB)) + { + if constexpr (CNS == NVME_IDENTIFY_CNS_CTRL) + { + controller = resource; + namespaceId = 0; + } + else if constexpr (CNS == NVME_IDENTIFY_CNS_NS) + { + auto subsys = resource->subsys.lock(); + assert(subsys && + "Fail to construct NS Identify Metric on expired subsystem"); + controller = subsys->getPrimaryController(); + resource->namespaceId(); + } + else + { + static_assert(false, "unsupported CNS for IdentifyMetric"); + } + } + + /* interface overrides */ + inline static constexpr std::string_view identifyIdentifier() noexcept + { + return "IdentifyMetric"; + } + constexpr std::string_view getIdentifier() const noexcept override + { + return identifyIdentifier(); + } + + bool isCacheValid() const noexcept override + { + return cacheValid; + } + std::tuple<std::chrono::time_point<ClockType>, + std::chrono::time_point<ClockType>, std::span<const uint8_t>> + getCache() const noexcept override + { + std::chrono::time_point<ClockType> start = std::get<0>(cache); + std::chrono::time_point<ClockType> stop = std::get<1>(cache); + auto& data = std::get<2>(cache); + return {start, stop, + std::span<const uint8_t>(data.begin(), data.end())}; + } + + /* internal overrides */ + private: + // read data from device and update the + // implemetation should extend this virtual function and do the real + // data fetch from device. + void readDevice(std::function<void(std::error_code ec, size_t size, + bool complete)>&& cb) noexcept override + { + auto startTime = ClockType::now(); + + auto cntrlPtr = controller.lock(); + assert(cntrlPtr && + cntrlPtr->status == NVMeControllerEnabled::Status::Enabled && + "NVMe Metric refresh requires enabled controller"); + assert(cntrlPtr->nvmeIntf && + "NVMe Metric refresh requires active MI interface"); + + cntrlPtr->nvmeIntf->adminIdentify( + cntrlPtr->nvmeCtrl, CNS, namespaceId, + 0 /* CNTID will not be used for CNS = 00h/01h */, + [weakSelf{std::move(this->weak_from_this())}, startTime, + path{cntrlPtr->path}, id{getIdentifier()}, cb{std::move(cb)}]( + const nvme_ex_ptr& ex, std::span<uint8_t> data) { + auto self = + std::dynamic_pointer_cast<IdentifyMetric<CNS, ClockType>>( + weakSelf.lock()); + if (!self) + { + // TODO: error_level = INFO + std::cerr << std::format( + "[{}, {}]metric released before callback\n", path, id); + // The Scheduler will clear the invalid cache Task + cb({}, 4096, true); + return; + } + if (ex) + { + if (self->errorCount < + IdentifyMetric<CNS, ClockType>::errorCountMax) + { + // TODO: error_level = WARN + std::cerr << std::format( + "[{}, {}]fail to fresh identify at retry({}): {}\n", + path, id, self->errorCount++, ex->description()); + + // Schedule the retry after 1s + cb(std::make_error_code(std::errc::io_error), 4096, false); + return; + } + std::cerr << std::format( + "[{}, {}]retry exhausted to fresh identify: {}\n", path, id, + ex->description()); + self->cacheValid = false; + // Notify the MatrixStore of the for the invalid Cache + self->completeCB(self); + + // Notify the RefreshCB with error code; the task will + // be reschuduled after this->interval + cb(std::make_error_code(std::errc::io_error), 4096, true); + return; + } + self->errorCount = 0; + self->cacheValid = true; + std::get<0>(self->cache) = startTime; + std::get<1>(self->cache) = ClockType::now(); + + auto& cacheData = std::get<2>(self->cache); + cacheData.clear(); + cacheData.insert(cacheData.begin(), data.begin(), data.end()); + + // notify the MetricStore for completion + self->completeCB(self); + + cb({}, data.size(), true); + }); + } + + /* member variables */ + std::weak_ptr<NVMeControllerEnabled> controller; + uint32_t namespaceId; + + std::function<void(std::shared_ptr<MetricBase<ClockType>>)> completeCB; + + std::tuple<std::chrono::time_point<ClockType>, + std::chrono::time_point<ClockType>, std::vector<uint8_t>> + cache; + bool cacheValid{false}; + unsigned errorCount{0}; + static constexpr unsigned errorCountMax = 5; +};
diff --git a/src/NVMeController.cpp b/src/NVMeController.cpp index ca604f9..6f270b1 100644 --- a/src/NVMeController.cpp +++ b/src/NVMeController.cpp
@@ -221,6 +221,9 @@ }; // NOLINTEND(performance-unnecessary-value-param) + using IdentifyMetric = + IdentifyMetric<NVME_IDENTIFY_CNS_CTRL, SchedulerClockType>; + using ErrorInfoMetric = ControllerLogPageMetric<NVME_LOG_LID_ERROR, SchedulerClockType>; using SMARTMetric = @@ -235,6 +238,13 @@ SchedulerClockType>; // NOLINTBEGIN(modernize-make-shared) + auto identify = std::shared_ptr<IdentifyMetric>(new IdentifyMetric( + this->shared_from_this(), scheduler, + // Identify info is persist throughout the controller lifecycle + // i.e. from start() to stop() + IdentifyMetric::ClockType::duration::max(), + std::bind_front(completeCB, IdentifyMetric::identifyIdentifier()))); + auto errorInfo = std::shared_ptr<ErrorInfoMetric>(new ErrorInfoMetric( this->weak_from_this(), scheduler, // TODO: configurable interval @@ -275,8 +285,9 @@ this->metricStore.emplace( this->NVMeController::io, this->NVMeController::conn, - this->NVMeController::path, std::move(errorInfo), std::move(smart), - std::move(fwSlot), std::move(selfTest), std::move(hostTelemetry)); + this->NVMeController::path, std::move(identify), std::move(errorInfo), + std::move(smart), std::move(fwSlot), std::move(selfTest), + std::move(hostTelemetry)); status = Status::Enabled; }
diff --git a/src/NVMeController.hpp b/src/NVMeController.hpp index 9295b52..018304e 100644 --- a/src/NVMeController.hpp +++ b/src/NVMeController.hpp
@@ -132,6 +132,8 @@ */ template <nvme_cmd_get_log_lid LID, class ClockType = std::chrono::steady_clock> class ControllerLogPageMetric; +template <nvme_identify_cns CNS, class ClockType> +class IdentifyMetric; /** * @brief A class for the NVMe controller that has been enabled (CC.EN = 1) @@ -166,6 +168,8 @@ private: template <nvme_cmd_get_log_lid LID, class ClockType> friend class ControllerLogPageMetric; + template <nvme_identify_cns CNS, class ClockType> + friend class IdentifyMetric; enum class Status : uint8_t {