| #pragma once |
| /** |
| * This file contains all cache implementation of types of Metrics |
| */ |
| |
| #include "NVMeCache.hpp" |
| #include "NVMeController.hpp" |
| |
| /** |
| * Metric implementation for LogPage |
| */ |
| |
| template <nvme_cmd_get_log_lid LID, class ClockType> |
| class ControllerLogPageMetric : public MetricBase<ClockType> |
| { |
| public: |
| ControllerLogPageMetric( |
| std::weak_ptr<NVMeControllerEnabled> controller, |
| std::shared_ptr<Scheduler<ClockType>> scheduler, |
| ClockType::duration interval, |
| std::function<void(std::shared_ptr<MetricBase<ClockType>>)>&& |
| completeCB) : |
| MetricBase<ClockType>(scheduler, interval), |
| controller(std::move(controller)), completeCB(std::move(completeCB)) |
| {} |
| |
| /* interface overrides */ |
| static constexpr std::string_view logpageIdentifier() noexcept |
| { |
| if constexpr (LID == NVME_LOG_LID_SMART) |
| { |
| return "SMARTMetric"; |
| } |
| else |
| { |
| static_assert(false, |
| "LID not supported by ControllerLogPageMetric"); |
| } |
| |
| return {}; |
| } |
| |
| constexpr std::string_view getIdentifier() const noexcept override |
| { |
| return logpageIdentifier(); |
| } |
| |
| 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 |
| { |
| return {}; |
| } |
| |
| /* 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->adminGetLogPage( |
| cntrlPtr->nvmeCtrl, LID, 0, 0, 0, |
| [weakSelf{std::move(this->weak_from_this())}, startTime, |
| path{cntrlPtr->path}, id{getIdentifier()}, cb{std::move(cb)}]( |
| const std::error_code& ec, std::span<uint8_t> data) { |
| auto self = std::dynamic_pointer_cast< |
| ControllerLogPageMetric<LID, ClockType>>(weakSelf.lock()); |
| if (!self) |
| { |
| // TODO: error_level = INFO |
| std::cerr << std::format( |
| "[{}, {}]metric released before callback", path, id); |
| // The Scheduler will clear the invalid cache Task |
| cb({}, 4096, true); |
| return; |
| } |
| if (ec) |
| { |
| if (self->errorCount < |
| ControllerLogPageMetric<LID>::errorCountMax) |
| { |
| // TODO: error_level = WARN |
| std::cerr << std::format( |
| "[{}, {}]fail to fresh log page at retry({})", path, id, |
| self->errorCount); |
| |
| // Schedule the retry after 1s |
| // TODO: costomize the retry delay for each log pages. |
| cb(std::make_error_code(std::errc::io_error), 4096, false); |
| return; |
| } |
| |
| std::cerr << std::format( |
| "[{}, {}]retry exhausted to fresh log page", path, id); |
| 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; |
| 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; |
| }; |