FRU: Invalidate cache when frus change
This creates a match for FruDevice changes and
update the cache when one changes.
Tested:
ipmitool fru list immediately after rescan
had correct values. fru list was faster
Change-Id: I2f332814bd1a0ef38a87dc31b8cd3c16fa6b8d76
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/src/storagecommands.cpp b/src/storagecommands.cpp
index 1887581..c2c1236 100644
--- a/src/storagecommands.cpp
+++ b/src/storagecommands.cpp
@@ -93,20 +93,16 @@
constexpr static const size_t maxMessageSize = 64;
constexpr static const size_t maxFruSdrNameSize = 16;
-using ManagedObjectType = boost::container::flat_map<
- sdbusplus::message::object_path,
- boost::container::flat_map<
- std::string, boost::container::flat_map<std::string, DbusVariant>>>;
-using ManagedEntry = std::pair<
- sdbusplus::message::object_path,
- boost::container::flat_map<
- std::string, boost::container::flat_map<std::string, DbusVariant>>>;
+using ObjectType = boost::container::flat_map<
+ std::string, boost::container::flat_map<std::string, DbusVariant>>;
+using ManagedObjectType =
+ boost::container::flat_map<sdbusplus::message::object_path, ObjectType>;
+using ManagedEntry = std::pair<sdbusplus::message::object_path, ObjectType>;
constexpr static const char* fruDeviceServiceName =
"xyz.openbmc_project.FruDevice";
constexpr static const char* entityManagerServiceName =
"xyz.openbmc_project.EntityManager";
-constexpr static const size_t cacheTimeoutSeconds = 30;
constexpr static const size_t writeTimeoutSeconds = 10;
constexpr static const char* chassisTypeRackMount = "23";
@@ -116,12 +112,13 @@
static std::vector<uint8_t> fruCache;
static uint8_t cacheBus = 0xFF;
static uint8_t cacheAddr = 0XFF;
+static uint8_t lastDevId = 0xFF;
static uint8_t writeBus = 0xFF;
static uint8_t writeAddr = 0XFF;
std::unique_ptr<phosphor::Timer> writeTimer = nullptr;
-std::unique_ptr<phosphor::Timer> cacheTimer = nullptr;
+static std::vector<sdbusplus::bus::match::match> fruMatches;
ManagedObjectType frus;
@@ -160,45 +157,13 @@
void createTimers()
{
-
writeTimer = std::make_unique<phosphor::Timer>(writeFru);
- cacheTimer = std::make_unique<phosphor::Timer>([]() { return; });
}
-ipmi::Cc replaceCacheFru(ipmi::Context::ptr ctx, uint8_t devId)
+void recalculateHashes()
{
- static uint8_t lastDevId = 0xFF;
-
- bool timerRunning = (cacheTimer != nullptr) && !cacheTimer->isExpired();
- if (lastDevId == devId && timerRunning)
- {
- return IPMI_CC_OK; // cache already up to date
- }
- // if timer is running, stop it and writeFru manually
- else if (timerRunning)
- {
- cacheTimer->stop();
- }
-
- cacheTimer->start(std::chrono::duration_cast<std::chrono::microseconds>(
- std::chrono::seconds(cacheTimeoutSeconds)));
-
- boost::system::error_code ec;
-
- frus = ctx->bus->yield_method_call<ManagedObjectType>(
- ctx->yield, ec, fruDeviceServiceName, "/",
- "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
- if (ec)
- {
- phosphor::logging::log<phosphor::logging::level::ERR>(
- "GetMangagedObjects for getSensorMap failed",
- phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
-
- return ipmi::ccResponseError;
- }
deviceHashes.clear();
-
// hash the object paths to create unique device id's. increment on
// collision
std::hash<std::string> hasher;
@@ -259,6 +224,35 @@
}
}
}
+}
+
+void replaceCacheFru(const std::shared_ptr<sdbusplus::asio::connection>& bus,
+ boost::asio::yield_context& yield,
+ const std::optional<std::string>& path = std::nullopt)
+{
+ boost::system::error_code ec;
+
+ frus = bus->yield_method_call<ManagedObjectType>(
+ yield, ec, fruDeviceServiceName, "/",
+ "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
+ if (ec)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "GetMangagedObjects for getSensorMap failed",
+ phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
+
+ return;
+ }
+ recalculateHashes();
+}
+
+ipmi::Cc getFru(ipmi::Context::ptr ctx, uint8_t devId)
+{
+ if (lastDevId == devId && devId != 0xFF)
+ {
+ return ipmi::ccSuccess;
+ }
+
auto deviceFind = deviceHashes.find(devId);
if (deviceFind == deviceHashes.end())
{
@@ -266,11 +260,12 @@
}
fruCache.clear();
- ec.clear();
cacheBus = deviceFind->second.first;
cacheAddr = deviceFind->second.second;
+ boost::system::error_code ec;
+
fruCache = ctx->bus->yield_method_call<std::vector<uint8_t>>(
ctx->yield, ec, fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
"xyz.openbmc_project.FruDeviceManager", "GetRawFru", cacheBus,
@@ -281,7 +276,6 @@
"Couldn't get raw fru",
phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
- lastDevId = 0xFF;
cacheBus = 0xFF;
cacheAddr = 0xFF;
return ipmi::ccResponseError;
@@ -291,6 +285,84 @@
return ipmi::ccSuccess;
}
+void writeFruIfRunning()
+{
+ if (!writeTimer->isRunning())
+ {
+ return;
+ }
+ writeTimer->stop();
+ writeFru();
+}
+
+void startMatch(void)
+{
+ if (fruMatches.size())
+ {
+ return;
+ }
+
+ fruMatches.reserve(2);
+
+ auto bus = getSdBus();
+ fruMatches.emplace_back(*bus,
+ "type='signal',arg0path='/xyz/openbmc_project/"
+ "FruDevice/',member='InterfacesAdded'",
+ [](sdbusplus::message::message& message) {
+ sdbusplus::message::object_path path;
+ ObjectType object;
+ try
+ {
+ message.read(path, object);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ return;
+ }
+ auto findType = object.find(
+ "xyz.openbmc_project.FruDevice");
+ if (findType == object.end())
+ {
+ return;
+ }
+ writeFruIfRunning();
+ frus[path] = object;
+ recalculateHashes();
+ lastDevId = 0xFF;
+ });
+
+ fruMatches.emplace_back(*bus,
+ "type='signal',arg0path='/xyz/openbmc_project/"
+ "FruDevice/',member='InterfacesRemoved'",
+ [](sdbusplus::message::message& message) {
+ sdbusplus::message::object_path path;
+ std::set<std::string> interfaces;
+ try
+ {
+ message.read(path, interfaces);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ return;
+ }
+ auto findType = interfaces.find(
+ "xyz.openbmc_project.FruDevice");
+ if (findType == interfaces.end())
+ {
+ return;
+ }
+ writeFruIfRunning();
+ frus.erase(path);
+ recalculateHashes();
+ lastDevId = 0xFF;
+ });
+
+ // call once to populate
+ boost::asio::spawn(*getIoContext(), [](boost::asio::yield_context yield) {
+ replaceCacheFru(getSdBus(), yield);
+ });
+}
+
/** @brief implements the read FRU data command
* @param fruDeviceId - FRU Device ID
* @param fruInventoryOffset - FRU Inventory Offset to write
@@ -310,7 +382,7 @@
return ipmi::responseInvalidFieldRequest();
}
- ipmi::Cc status = replaceCacheFru(ctx, fruDeviceId);
+ ipmi::Cc status = getFru(ctx, fruDeviceId);
if (status != ipmi::ccSuccess)
{
@@ -361,7 +433,7 @@
size_t writeLen = dataToWrite.size();
- ipmi::Cc status = replaceCacheFru(ctx, fruDeviceId);
+ ipmi::Cc status = getFru(ctx, fruDeviceId);
if (status != ipmi::ccSuccess)
{
return ipmi::response(status);
@@ -466,12 +538,7 @@
return ipmi::responseInvalidFieldRequest();
}
- ipmi::Cc status = replaceCacheFru(ctx, fruDeviceId);
-
- if (status != IPMI_CC_OK)
- {
- return ipmi::response(status);
- }
+ getFru(ctx, fruDeviceId);
constexpr uint8_t accessType =
static_cast<uint8_t>(GetFRUAreaAccessType::byte);
@@ -481,11 +548,6 @@
ipmi_ret_t getFruSdrCount(ipmi::Context::ptr ctx, size_t& count)
{
- ipmi_ret_t ret = replaceCacheFru(ctx, 0);
- if (ret != IPMI_CC_OK)
- {
- return ret;
- }
count = deviceHashes.size();
return IPMI_CC_OK;
}
@@ -493,11 +555,6 @@
ipmi_ret_t getFruSdrs(ipmi::Context::ptr ctx, size_t index,
get_sdr::SensorDataFruRecord& resp)
{
- ipmi_ret_t ret = replaceCacheFru(ctx, 0); // this will update the hash list
- if (ret != IPMI_CC_OK)
- {
- return ret;
- }
if (deviceHashes.size() < index)
{
return IPMI_CC_INVALID_FIELD_REQUEST;
@@ -1244,6 +1301,8 @@
void registerStorageFunctions()
{
createTimers();
+ startMatch();
+
// <Get FRU Inventory Area Info>
ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
ipmi::storage::cmdGetFruInventoryAreaInfo,