blob: 824205f074f544a6c990630e981d5af9fd2dff6b [file]
#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;
};