Add lifetime property for drives interface
This will set the lifetime property when the eStoraged object is
created. This change does not expect the lifetime to change.
Tested:
busctl introspect xyz.openbmc_project.eStoraged.mmcblk0 /xyz/openbmc_project/inventory/storage/mmcblk0
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
org.freedesktop.DBus.Introspectable interface - - -
.Introspect method - s -
org.freedesktop.DBus.Peer interface - - -
.GetMachineId method - s -
.Ping method - - -
org.freedesktop.DBus.Properties interface - - -
.Get method ss v -
.GetAll method s a{sv} -
.Set method ssv - -
.PropertiesChanged signal sa{sv}as - -
xyz.openbmc_project.Inventory.Item.Drive interface - - -
.Capacity property t (top secret) emits-change
.PredictedMediaLifeLeftPercent property y 100 emits-change
xyz.openbmc_project.Inventory.Item.Volume interface - - -
.ChangePassword method ayay - -
.Erase method s - -
.FormatLuks method ays - -
.Lock method - - -
.Unlock method ay - -
Signed-off-by: John Edward Broadbent <jebr@google.com>
Change-Id: Ifbbed7d81c55e3edbe519c2b1048b5d1731fbb0e
diff --git a/src/estoraged.cpp b/src/estoraged.cpp
index 6225f91..9646139 100644
--- a/src/estoraged.cpp
+++ b/src/estoraged.cpp
@@ -33,7 +33,7 @@
EStoraged::EStoraged(sdbusplus::asio::object_server& server,
const std::string& devPath, const std::string& luksName,
- uint64_t size,
+ uint64_t size, uint8_t lifeTime,
std::unique_ptr<CryptsetupInterface> cryptInterface,
std::unique_ptr<FilesystemInterface> fsInterface) :
devPath(devPath),
@@ -77,6 +77,8 @@
driveInterface = objectServer.add_interface(
path, "xyz.openbmc_project.Inventory.Item.Drive");
driveInterface->register_property("Capacity", size);
+ driveInterface->register_property("PredictedMediaLifeLeftPercent",
+ lifeTime);
volumeInterface->initialize();
driveInterface->initialize();
diff --git a/src/main.cpp b/src/main.cpp
index aea7904..ed360fb 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -23,15 +23,20 @@
" -b <blockDevice> The phyical encrypted device\n"
" If omitted, default is /dev/mmcblk0.\n"
" -c <containerName> The LUKS container name to be created\n"
- " If omitted, default is luks-<devName>";
+ " If omitted, default is luks-<devName>"
+ " -s <sysfsDevice> The interface to kernel data\n"
+ " structures dealing with this drive.\n"
+ " If omitted, default is\n"
+ " /sys/block/mmcblk0/device/\n";
}
int main(int argc, char** argv)
{
std::string physicalBlockDev = "/dev/mmcblk0";
+ std::string sysfsDev = "/sys/block/mmcblk0/device";
std::string containerBlockDev;
int opt = 0;
- while ((opt = getopt(argc, argv, "b:c:")) != -1)
+ while ((opt = getopt(argc, argv, "b:c:s:")) != -1)
{
switch (opt)
{
@@ -41,6 +46,9 @@
case 'c':
containerBlockDev = optarg;
break;
+ case 's':
+ sysfsDev = optarg;
+ break;
default:
usage(*argv);
exit(EXIT_FAILURE);
@@ -68,7 +76,8 @@
estoraged::EStoraged esObject{
server, physicalBlockDev, containerBlockDev,
- estoraged::util::Util::findSizeOfBlockDevice(physicalBlockDev)};
+ estoraged::util::findSizeOfBlockDevice(physicalBlockDev),
+ estoraged::util::findPredictedMediaLifeLeftPercent(sysfsDev)};
lg2::info("Storage management service is running", "REDFISH_MESSAGE_ID",
std::string("OpenBMC.1.0.ServiceStarted"));
diff --git a/src/test/estoraged_test.cpp b/src/test/estoraged_test.cpp
index 05f872f..b98144e 100644
--- a/src/test/estoraged_test.cpp
+++ b/src/test/estoraged_test.cpp
@@ -39,6 +39,7 @@
const char* testFileName = "testfile";
const char* testLuksDevName = "testfile_luksDev";
const uint64_t testSize = 24;
+ const uint8_t testLifeTime = 25;
std::ofstream testFile;
const char* testPath = "/test/openbmc_project/storage/test_dev";
const char* estoragedInterface =
@@ -83,7 +84,7 @@
esObject = std::make_unique<estoraged::EStoraged>(
*objectServer, testFileName, testLuksDevName, testSize,
- std::move(cryptIface), std::move(fsIface));
+ testLifeTime, std::move(cryptIface), std::move(fsIface));
}
void TearDown() override
diff --git a/src/test/meson.build b/src/test/meson.build
index 29cdda4..180d9d5 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -8,6 +8,7 @@
'erase/crypto_test',
'erase/sanitize_test',
'estoraged_test',
+ 'util_test',
]
test_eStoraged_headers = include_directories('include')
diff --git a/src/test/util_test.cpp b/src/test/util_test.cpp
new file mode 100644
index 0000000..8f53510
--- /dev/null
+++ b/src/test/util_test.cpp
@@ -0,0 +1,52 @@
+#include <util.hpp>
+
+#include <fstream>
+
+#include <gmock/gmock-matchers.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace estoraged_test
+{
+using estoraged::util::findPredictedMediaLifeLeftPercent;
+
+TEST(utilTest, passFindPredictedMediaLife)
+{
+ std::string prefixName = ".";
+ std::string testFileName = prefixName + "/life_time";
+ std::ofstream testFile;
+ testFile.open(testFileName,
+ std::ios::out | std::ios::binary | std::ios::trunc);
+ testFile << "0x07 0x04";
+ testFile.close();
+ EXPECT_EQ(findPredictedMediaLifeLeftPercent(prefixName), 40);
+}
+
+TEST(utilTest, estimatesSame)
+{
+
+ std::string prefixName = ".";
+ std::string testFileName = prefixName + "/life_time";
+ std::ofstream testFile;
+ testFile.open(testFileName,
+ std::ios::out | std::ios::binary | std::ios::trunc);
+ testFile << "0x04 0x04";
+ testFile.close();
+
+ EXPECT_EQ(findPredictedMediaLifeLeftPercent(prefixName), 70);
+}
+
+TEST(utilTest, estimatesNotAvailable)
+{
+
+ std::string prefixName = ".";
+ std::string testFileName = prefixName + "/life_time";
+ std::ofstream testFile;
+ testFile.open(testFileName,
+ std::ios::out | std::ios::binary | std::ios::trunc);
+ testFile.close();
+
+ EXPECT_EQ(findPredictedMediaLifeLeftPercent(prefixName), 255);
+}
+
+} // namespace estoraged_test
diff --git a/src/util.cpp b/src/util.cpp
index 7200d0a..acab3d8 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -8,7 +8,9 @@
#include <stdplus/handle/managed.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
-#include <string_view>
+#include <fstream>
+#include <iostream>
+#include <string>
namespace estoraged
{
@@ -17,7 +19,7 @@
using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
using ::stdplus::fd::ManagedFd;
-uint64_t Util::findSizeOfBlockDevice(const std::string& devPath)
+uint64_t findSizeOfBlockDevice(const std::string& devPath)
{
ManagedFd fd;
uint64_t bytes = 0;
@@ -38,6 +40,55 @@
return bytes;
}
+uint8_t findPredictedMediaLifeLeftPercent(const std::string& sysfsPath)
+{
+ // The eMMC spec defines two estimates for the life span of the device
+ // in the extended CSD field 269 and 268, named estimate A and estimate B.
+ // Linux exposes the values in the /life_time node.
+ // estimate A is for A type memory
+ // estimate B is for B type memory
+ //
+ // the estimate are encoded as such
+ // 0x01 <=> 0% - 10% device life time used
+ // 0x02 <=> 10% -20% device life time used
+ // ...
+ // 0x0A <=> 90% - 100% device life time used
+ // 0x0B <=> Exceeded its maximum estimated device life time
+
+ uint16_t estA = 0, estB = 0;
+ std::ifstream lifeTimeFile;
+ try
+ {
+ lifeTimeFile.open(sysfsPath + "/life_time", std::ios_base::in);
+ lifeTimeFile >> std::hex >> estA;
+ lifeTimeFile >> std::hex >> estB;
+ if ((estA == 0) || (estA > 11) || (estB == 0) || (estB > 11))
+ {
+ throw InternalFailure();
+ }
+ }
+ catch (...)
+ {
+ lg2::error("Unable to read sysfs", "REDFISH_MESSAGE_ID",
+ std::string("OpenBMC.0.1.DriveEraseFailure"));
+ lifeTimeFile.close();
+ return 255;
+ }
+ lifeTimeFile.close();
+ // we are returning lowest LifeLeftPercent
+ uint8_t maxLifeUsed = 0;
+ if (estA > estB)
+ {
+ maxLifeUsed = estA;
+ }
+ else
+ {
+ maxLifeUsed = estB;
+ }
+
+ return static_cast<uint8_t>(11 - maxLifeUsed) * 10;
+}
+
} // namespace util
} // namespace estoraged