| #pragma once |
| |
| /** |
| * This file defines the MetricStore class, which is the DBus inplemention to |
| * interact with a collection of NVMe Metrics Cache for a Storage |
| * object(e.g. NVMe Contoller) |
| */ |
| |
| #include "NVMeCache.hpp" |
| |
| #include <boost/asio.hpp> |
| #include <boost/asio/write.hpp> |
| #include <boost/endian.hpp> |
| #include <sdbusplus/asio/connection.hpp> |
| #include <sdbusplus/asio/object_server.hpp> |
| #include <sdbusplus/sdbus.hpp> |
| #include <sdbusplus/server.hpp> |
| #include <xyz/openbmc_project/NVMe/MetricStore/server.hpp> |
| |
| #include <functional> |
| #include <iostream> |
| #include <memory> |
| #include <string> |
| #include <tuple> |
| #include <unordered_map> |
| |
| constexpr bool debug = false; |
| |
| struct MetricHeader |
| { |
| boost::endian::little_uint32_t lens; // Header length |
| boost::endian::little_uint16_t version; // Header version |
| uint8_t dataFormat; // the data format. 0x00 = Raw NVMe format |
| uint8_t reserved; |
| boost::endian::little_uint64_t |
| startTime; // time stamp when the Metric started the retrival from the |
| // device |
| boost::endian::little_uint64_t |
| finishTime; // time stamp when the Metric finished the retrival from |
| // the device |
| }; |
| |
| class MetricStore : |
| public sdbusplus::xyz::openbmc_project::NVMe::server::MetricStore |
| { |
| public: |
| template <class... Args> |
| requires(std::is_base_of_v<MetricBase<std::chrono::steady_clock>, |
| Args> && |
| ...) |
| MetricStore(boost::asio::io_context& io, |
| const std::shared_ptr<sdbusplus::asio::connection>& conn, |
| const std::string& objectPath, |
| std::shared_ptr<Args>&&... metrics) : |
| sdbusplus::xyz::openbmc_project::NVMe::server::MetricStore( |
| dynamic_cast<sdbusplus::bus_t&>(*conn), objectPath.c_str()), |
| path(objectPath), io(io), |
| metricStore{{std::string{metrics ? metrics->getIdentifier() : ""}, |
| std::move(metrics)}...}, |
| validFlag(std::make_shared<bool>()) |
| { |
| metricStore.erase(""); |
| auto vuMetrics = getVendorMatrics(path); |
| for (auto& metric : vuMetrics) |
| { |
| if (!metric) |
| { |
| continue; |
| } |
| auto [itr, res] = metricStore.try_emplace( |
| std::string{metric->getIdentifier()}, std::move(metric)); |
| if (!res) |
| { |
| std::cerr << std::format( |
| "[{}, {}] drop a duplicated vendor metic\n", path, |
| itr->first); |
| } |
| } |
| |
| std::vector<std::string> idCollection; |
| decltype(validFlag)::weak_type weakFlag = validFlag; |
| for (auto& [identifier, metric] : metricStore) |
| { |
| idCollection.push_back(identifier); |
| std::weak_ptr<MetricBase<std::chrono::steady_clock>> weakMetric = |
| metric; |
| metric->registerCompleteCB([this, weakFlag, identifier, weakMetric, |
| path{objectPath}](std::error_code ec) { |
| // use validFlag to protect this call |
| if (weakFlag.expired()) |
| { |
| // TODO: log_level = INFO |
| std::cerr << std::format( |
| "[{}]MetricStore interface expired, update signal skipped.\n", |
| path); |
| return; |
| } |
| auto metric = weakMetric.lock(); |
| if (ec || !metric) |
| { |
| // broadcast invalid metric signal |
| this->signalHelper(identifier, {}); |
| } |
| this->signalHelper(identifier, metric); |
| }); |
| metric->refresh({}); |
| } |
| metricCollection(std::move(idCollection), true); |
| emit_added(); |
| } |
| ~MetricStore() override |
| { |
| emit_removed(); |
| } |
| |
| private: |
| static std::vector<std::shared_ptr<MetricBase<>>> |
| getVendorMatrics(const std::string& path); |
| /** |
| * @brief Writes to file descriptor after getting results from the |
| * registered callback function |
| * |
| * @param[in] data: Vector of raw bytes (header + actual data) to be written |
| * to file descriptor |
| * |
| * @return sdbusplus::message::unix_fd: Returns the read end of the pipe |
| */ |
| sdbusplus::message::unix_fd writeToFd(std::span<const uint8_t> header, |
| std::span<const uint8_t> data); |
| |
| /** |
| * @brief Gets Metric using a unique metric name. Metric must be registered |
| * with that unique name |
| * |
| * @param[in] name: Registered metric name |
| * |
| * @return file descriptor pointing to the data |
| */ |
| sdbusplus::message::unix_fd getMetric(std::string name) override; // NOLINT |
| |
| /** |
| * @brief A helper function to signal MetricUpdated |
| * |
| * @param[in] name: metric name |
| * @param[in] metric: metric instance. null instance will trigger invalid |
| * signal |
| */ |
| void signalHelper( |
| std::string_view name, |
| const std::shared_ptr<MetricBase<std::chrono::steady_clock>>& |
| metric) noexcept; |
| |
| std::string path; |
| boost::asio::io_context& io; |
| std::unordered_map<std::string, std::shared_ptr<MetricBase<>>> metricStore; |
| // A flag to check the validation of this ptr when this is captured by async |
| // function. No other oject should seize the strong ptr of the flag other |
| // than this instance. |
| std::shared_ptr<bool> validFlag; |
| }; |