blob: 7af790aa598f6b67a518816ec69d1dba1b1a47cb [file] [log] [blame]
/*
* Copyright (C) 2020 CESNET, https://photonics.cesnet.cz/
*
* Written by Tomáš Pecka <tomas.pecka@fit.cvut.cz>
*
*/
#include <filesystem>
#include <string>
#include <utility>
#include <vector>
#include "EMMC.h"
#include "utils/io.h"
#include "utils/log.h"
namespace velia::ietf_hardware::sysfs {
/** @class EMMC
@short Implements access to EMMC specific data from sysfs
This class provides EMMC (v5 +) specific data from sysfs
Code based on eMMC 5.1 docs https://www.jedec.org/sites/default/files/docs/JESD84-B51.pdf and kernel code from drivers/mmc/core/mmc.c.
*/
using namespace std::literals;
namespace {
/** @brief Report life time of the emmc device. This is a property constructed from 'life_time' and 'pre_eol_information' values reported by kernel.
*
* Kernel provides three different health information values w.r.t. the eMMC standard (>=5, https://www.jedec.org/sites/default/files/docs/JESD84-B51.pdf):
* - "Device life time estimation type A" (file 'life_time', first hex-encoded value)
* - "Device life time estimation type B" (file 'life_time', second hex-encoded value)
* - "Pre EOL information" (file 'pre_eol_information', single hex-encoded value)
*
* The first and second values provide an estimated indication about the device life time that is reflected by the averaged wear out of memory of
* type A (SLC) and type B (MLC) relative to its maximum estimated device life time.
* - 0x01...0x0A - correspond to % of lifetime used: 0x01 is 0-10%, 0x02 is 10-20%, ... , 0x0A is 90% - 100%
* - 0x0B is over 100 %
* - 0x00 is undefined
*
* Both values are always reported according to kernel code (drivers/mmc/core/mmc.c). The standard does not say anything regarding why both are reported.
*
* The EOL information provides indication about device life time reflected by average reserved blocks
* - 0x00 is undefined
* - 0x01 is normal
* - 0x02 is a warning - consumed 80 % of reserved blocks
* - 0x03 is urgent (not stated in the linked PDF but sometimes referred as 90 % used)
*
* We have decided to merge these values into one (so that customer does not have to be an eMMC expert) percentual value about health.
* Therefore we report maximum of those percentual values.
*/
std::string processLifeTimeProperty(const std::filesystem::path& rootDir)
{
std::vector<uint32_t> lifeTime = velia::utils::readFileWords(rootDir / "life_time", 2);
std::vector<uint32_t> eol = velia::utils::readFileWords(rootDir / "pre_eol_info", 1);
std::vector<uint32_t> res;
// interpret lifeTimeData as percentual health
res.push_back(lifeTime[0] != 0x00 ? (lifeTime[0] - 1) * 10 : 0x00);
res.push_back(lifeTime[1] != 0x00 ? (lifeTime[1] - 1) * 10 : 0x00);
// interpret eol data as percentual health
switch (eol[0]) {
case 0x00:
case 0x01:
res.push_back(0);
break;
case 0x02:
res.push_back(80);
break;
case 0x03:
res.push_back(90);
break;
}
return std::to_string(*std::max_element(res.begin(), res.end()));
}
}
/**
* @short Constructs a EMMC driver for EMMC entries
* @param root A path to the EMMC device, e.g., /sys/block/mmcblk0/device
* */
EMMC::EMMC(std::filesystem::path blockDevDir)
: m_log(spdlog::get("hardware"))
, m_root(std::move(blockDevDir))
{
m_log->trace("EMMC driver initialized for '{}'", std::string(m_root));
}
EMMC::~EMMC() = default;
/** @short Return attributes provided by EMMC sysfs driver */
EMMC::Attributes EMMC::attributes() const
{
static const std::vector<std::string> ATTRIBUTE_FILES = {"serial", "date", "name"};
Attributes result;
for (const auto& filename : ATTRIBUTE_FILES) {
result.insert(std::make_pair(filename, velia::utils::readFileString(m_root / filename)));
};
result.emplace("life_time", processLifeTimeProperty(m_root));
return result;
}
}