PEL: Implement respository pruning
This adds a prune() public method on the Repository class to remove PELs
down to at most 90% of capacity and then down to 80% of the maximum
number of PELs if there were more than the maximum.
It does the first set of pruning by placing each PEL is one of 4
categories, and then reducing the total size of each category. The
categories are:
* BMC informational PELs - reduced to 15% of max
* BMC non-informational PELs - reduced to 30% of max
* non-BMC informational PELs - reduced to 15% of max
* non-BMC non-informational PELs - reduced to 30% of max
Within each category, PELs are removed oldest first, and also 4 passes
are made through the PELs, only removing PELs that meet a specific
requirement each pass, stopping as soon as the category limit is
reached.
The pass requirements are:
* Pass 1: Only remove HMC acked PELs
* Pass 2: Only remove OS acked PELs
* Pass 3: Only remove host sent PELs
* Pass 4: Remove any PEL
After the 4 passes on the 4 categories are done then the number of PELs
remaining is checked against the maximum number. If it is more than the
maximum, it will remove the PELs down to 80% of that limit using the
same 4 passes as above. This is done to keep the number of PELs down to
a manageable number when there are a lot of small PELs that don't engage
the size based pruning.
The pruning code doesn't just bring the size or number of PELs to just
below their limit, but rather a percentage below, so that it won't get
into a situation where the algorithm has to run on the repository every
single time a PEL is added.
The OpenBMC event log corresponding to the PELs are not removed. That
is left to other code.
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I24da611c095fd3b22b6b1ffab52d919cac5f68b4
diff --git a/extensions/openpower-pels/repository.cpp b/extensions/openpower-pels/repository.cpp
index 8458fcc..e608927 100644
--- a/extensions/openpower-pels/repository.cpp
+++ b/extensions/openpower-pels/repository.cpp
@@ -499,5 +499,180 @@
}
}
+std::vector<Repository::AttributesReference>
+ Repository::getAllPELAttributes(SortOrder order) const
+{
+ std::vector<Repository::AttributesReference> attributes;
+
+ std::for_each(
+ _pelAttributes.begin(), _pelAttributes.end(),
+ [&attributes](auto& pelEntry) { attributes.push_back(pelEntry); });
+
+ std::sort(attributes.begin(), attributes.end(),
+ [order](const auto& left, const auto& right) {
+ if (order == SortOrder::ascending)
+ {
+ return left.get().second.path < right.get().second.path;
+ }
+ return left.get().second.path > right.get().second.path;
+ });
+
+ return attributes;
+}
+
+std::vector<uint32_t> Repository::prune()
+{
+ std::vector<uint32_t> obmcLogIDs;
+ std::string msg = "Pruning PEL repository that takes up " +
+ std::to_string(_sizes.total) + " bytes and has " +
+ std::to_string(_pelAttributes.size()) + " PELs";
+ log<level::INFO>(msg.c_str());
+
+ // Set up the 5 functions to check if the PEL category
+ // is still over its limits.
+
+ // BMC informational PELs should only take up 15%
+ IsOverLimitFunc overBMCInfoLimit = [this]() {
+ return _sizes.bmcInfo > _maxRepoSize * 15 / 100;
+ };
+
+ // BMC non informational PELs should only take up 30%
+ IsOverLimitFunc overBMCNonInfoLimit = [this]() {
+ return _sizes.bmcServiceable > _maxRepoSize * 30 / 100;
+ };
+
+ // Non BMC informational PELs should only take up 15%
+ IsOverLimitFunc overNonBMCInfoLimit = [this]() {
+ return _sizes.nonBMCInfo > _maxRepoSize * 15 / 100;
+ };
+
+ // Non BMC non informational PELs should only take up 15%
+ IsOverLimitFunc overNonBMCNonInfoLimit = [this]() {
+ return _sizes.nonBMCServiceable > _maxRepoSize * 30 / 100;
+ };
+
+ // Bring the total number of PELs down to 80% of the max
+ IsOverLimitFunc tooManyPELsLimit = [this]() {
+ return _pelAttributes.size() > _maxNumPELs * 80 / 100;
+ };
+
+ // Set up the functions to determine which category a PEL is in.
+ // TODO: Return false in these functions if a PEL caused a guard record.
+
+ // A BMC informational PEL
+ IsPELTypeFunc isBMCInfo = [](const PELAttributes& pel) {
+ return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) &&
+ !Repository::isServiceableSev(pel);
+ };
+
+ // A BMC non informational PEL
+ IsPELTypeFunc isBMCNonInfo = [](const PELAttributes& pel) {
+ return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) &&
+ Repository::isServiceableSev(pel);
+ };
+
+ // A non BMC informational PEL
+ IsPELTypeFunc isNonBMCInfo = [](const PELAttributes& pel) {
+ return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) &&
+ !Repository::isServiceableSev(pel);
+ };
+
+ // A non BMC non informational PEL
+ IsPELTypeFunc isNonBMCNonInfo = [](const PELAttributes& pel) {
+ return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) &&
+ Repository::isServiceableSev(pel);
+ };
+
+ // When counting PELs, count every PEL
+ IsPELTypeFunc isAnyPEL = [](const PELAttributes& pel) { return true; };
+
+ // Check all 4 categories, which will result in at most 90%
+ // usage (15 + 30 + 15 + 30).
+ removePELs(overBMCInfoLimit, isBMCInfo, obmcLogIDs);
+ removePELs(overBMCNonInfoLimit, isBMCNonInfo, obmcLogIDs);
+ removePELs(overNonBMCInfoLimit, isNonBMCInfo, obmcLogIDs);
+ removePELs(overNonBMCNonInfoLimit, isNonBMCNonInfo, obmcLogIDs);
+
+ // After the above pruning check if there are still too many PELs,
+ // which can happen depending on PEL sizes.
+ if (_pelAttributes.size() > _maxNumPELs)
+ {
+ removePELs(tooManyPELsLimit, isAnyPEL, obmcLogIDs);
+ }
+
+ if (!obmcLogIDs.empty())
+ {
+ std::string msg = "Number of PELs removed to save space: " +
+ std::to_string(obmcLogIDs.size());
+ log<level::INFO>(msg.c_str());
+ }
+
+ return obmcLogIDs;
+}
+
+void Repository::removePELs(IsOverLimitFunc& isOverLimit,
+ IsPELTypeFunc& isPELType,
+ std::vector<uint32_t>& removedBMCLogIDs)
+{
+ if (!isOverLimit())
+ {
+ return;
+ }
+
+ auto attributes = getAllPELAttributes(SortOrder::ascending);
+
+ // Make 4 passes on the PELs, stopping as soon as isOverLimit
+ // returns false.
+ // Pass 1: only delete HMC acked PELs
+ // Pass 2: only delete OS acked PELs
+ // Pass 3: only delete PHYP sent PELs
+ // Pass 4: delete all PELs
+ static const std::vector<std::function<bool(const PELAttributes& pel)>>
+ stateChecks{[](const auto& pel) {
+ return pel.hmcState == TransmissionState::acked;
+ },
+
+ [](const auto& pel) {
+ return pel.hostState == TransmissionState::acked;
+ },
+
+ [](const auto& pel) {
+ return pel.hostState == TransmissionState::sent;
+ },
+
+ [](const auto& pel) { return true; }};
+
+ for (const auto& stateCheck : stateChecks)
+ {
+ for (auto it = attributes.begin(); it != attributes.end();)
+ {
+ const auto& pel = it->get();
+ if (isPELType(pel.second) && stateCheck(pel.second))
+ {
+ auto removedID = pel.first.obmcID.id;
+ remove(pel.first);
+
+ removedBMCLogIDs.push_back(removedID);
+
+ attributes.erase(it);
+
+ if (!isOverLimit())
+ {
+ break;
+ }
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ if (!isOverLimit())
+ {
+ break;
+ }
+ }
+}
+
} // namespace pels
} // namespace openpower