xref: /openbmc/phosphor-logging/extensions/openpower-pels/repository.cpp (revision d0ccda3c2a36625d30a6c3b70ed0216694102fed)
1 /**
2  * Copyright © 2019 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "repository.hpp"
17 
18 #include <fcntl.h>
19 #include <sys/stat.h>
20 
21 #include <phosphor-logging/log.hpp>
22 #include <xyz/openbmc_project/Common/File/error.hpp>
23 
24 #include <fstream>
25 
26 namespace openpower
27 {
28 namespace pels
29 {
30 
31 namespace fs = std::filesystem;
32 using namespace phosphor::logging;
33 namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
34 
35 constexpr size_t warningPercentage = 95;
36 
37 /**
38  * @brief Returns the amount of space the file uses on disk.
39  *
40  * This is different than just the regular size of the file.
41  *
42  * @param[in] file - The file to get the size of
43  *
44  * @return size_t The disk space the file uses
45  */
46 size_t getFileDiskSize(const std::filesystem::path& file)
47 {
48     constexpr size_t statBlockSize = 512;
49     struct stat statData;
50     auto rc = stat(file.c_str(), &statData);
51     if (rc != 0)
52     {
53         auto e = errno;
54         std::string msg = "call to stat() failed on " + file.native() +
55                           " with errno " + std::to_string(e);
56         log<level::ERR>(msg.c_str());
57         abort();
58     }
59 
60     return statData.st_blocks * statBlockSize;
61 }
62 
63 Repository::Repository(const std::filesystem::path& basePath, size_t repoSize,
64                        size_t maxNumPELs) :
65     _logPath(basePath / "logs"),
66     _maxRepoSize(repoSize), _maxNumPELs(maxNumPELs),
67     _archivePath(basePath / "logs" / "archive")
68 {
69     if (!fs::exists(_logPath))
70     {
71         fs::create_directories(_logPath);
72     }
73 
74     if (!fs::exists(_archivePath))
75     {
76         fs::create_directories(_archivePath);
77     }
78 
79     restore();
80 }
81 
82 void Repository::restore()
83 {
84     for (auto& dirEntry : fs::directory_iterator(_logPath))
85     {
86         try
87         {
88             if (!fs::is_regular_file(dirEntry.path()))
89             {
90                 continue;
91             }
92 
93             std::ifstream file{dirEntry.path()};
94             std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
95                                       std::istreambuf_iterator<char>()};
96             file.close();
97 
98             PEL pel{data};
99             if (pel.valid())
100             {
101                 // If the host hasn't acked it, reset the host state so
102                 // it will get sent up again.
103                 if (pel.hostTransmissionState() == TransmissionState::sent)
104                 {
105                     pel.setHostTransmissionState(TransmissionState::newPEL);
106                     try
107                     {
108                         write(pel, dirEntry.path());
109                     }
110                     catch (const std::exception& e)
111                     {
112                         log<level::ERR>(
113                             "Failed to save PEL after updating host state",
114                             entry("PELID=0x%X", pel.id()));
115                     }
116                 }
117 
118                 PELAttributes attributes{
119                     dirEntry.path(),
120                     getFileDiskSize(dirEntry.path()),
121                     pel.privateHeader().creatorID(),
122                     pel.userHeader().subsystem(),
123                     pel.userHeader().severity(),
124                     pel.userHeader().actionFlags(),
125                     pel.hostTransmissionState(),
126                     pel.hmcTransmissionState(),
127                     pel.plid(),
128                     pel.getDeconfigFlag(),
129                     pel.getGuardFlag(),
130                     getMillisecondsSinceEpoch(
131                         pel.privateHeader().createTimestamp())};
132 
133                 using pelID = LogID::Pel;
134                 using obmcID = LogID::Obmc;
135                 _pelAttributes.emplace(
136                     LogID(pelID(pel.id()), obmcID(pel.obmcLogID())),
137                     attributes);
138 
139                 updateRepoStats(attributes, true);
140             }
141             else
142             {
143                 log<level::ERR>(
144                     "Found invalid PEL file while restoring.  Removing.",
145                     entry("FILENAME=%s", dirEntry.path().c_str()));
146                 fs::remove(dirEntry.path());
147             }
148         }
149         catch (const std::exception& e)
150         {
151             log<level::ERR>("Hit exception while restoring PEL File",
152                             entry("FILENAME=%s", dirEntry.path().c_str()),
153                             entry("ERROR=%s", e.what()));
154         }
155     }
156 
157     // Get size of archive folder
158     for (auto& dirEntry : fs::directory_iterator(_archivePath))
159     {
160         _archiveSize += getFileDiskSize(dirEntry);
161     }
162 }
163 
164 std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time)
165 {
166     char name[50];
167     sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB,
168             time.yearLSB, time.month, time.day, time.hour, time.minutes,
169             time.seconds, time.hundredths, pelID);
170     return std::string{name};
171 }
172 
173 void Repository::add(std::unique_ptr<PEL>& pel)
174 {
175     pel->setHostTransmissionState(TransmissionState::newPEL);
176     pel->setHMCTransmissionState(TransmissionState::newPEL);
177 
178     auto path = _logPath / getPELFilename(pel->id(), pel->commitTime());
179 
180     write(*(pel.get()), path);
181 
182     PELAttributes attributes{
183         path,
184         getFileDiskSize(path),
185         pel->privateHeader().creatorID(),
186         pel->userHeader().subsystem(),
187         pel->userHeader().severity(),
188         pel->userHeader().actionFlags(),
189         pel->hostTransmissionState(),
190         pel->hmcTransmissionState(),
191         pel->plid(),
192         pel->getDeconfigFlag(),
193         pel->getGuardFlag(),
194         getMillisecondsSinceEpoch(pel->privateHeader().createTimestamp())};
195 
196     using pelID = LogID::Pel;
197     using obmcID = LogID::Obmc;
198     _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())),
199                            attributes);
200 
201     _lastPelID = pel->id();
202 
203     updateRepoStats(attributes, true);
204 
205     processAddCallbacks(*pel);
206 }
207 
208 void Repository::write(const PEL& pel, const fs::path& path)
209 {
210     std::ofstream file{path, std::ios::binary};
211 
212     if (!file.good())
213     {
214         // If this fails, the filesystem is probably full so it isn't like
215         // we could successfully create yet another error log here.
216         auto e = errno;
217         fs::remove(path);
218         log<level::ERR>("Unable to open PEL file for writing",
219                         entry("ERRNO=%d", e), entry("PATH=%s", path.c_str()));
220         throw file_error::Open();
221     }
222 
223     auto data = pel.data();
224     file.write(reinterpret_cast<const char*>(data.data()), data.size());
225 
226     if (file.fail())
227     {
228         // Same note as above about not being able to create an error log
229         // for this case even if we wanted.
230         auto e = errno;
231         file.close();
232         fs::remove(path);
233         log<level::ERR>("Unable to write PEL file", entry("ERRNO=%d", e),
234                         entry("PATH=%s", path.c_str()));
235         throw file_error::Write();
236     }
237 }
238 
239 std::optional<Repository::LogID> Repository::remove(const LogID& id)
240 {
241     auto pel = findPEL(id);
242     if (pel == _pelAttributes.end())
243     {
244         return std::nullopt;
245     }
246 
247     LogID actualID = pel->first;
248     updateRepoStats(pel->second, false);
249 
250     log<level::DEBUG>("Removing PEL from repository",
251                       entry("PEL_ID=0x%X", actualID.pelID.id),
252                       entry("OBMC_LOG_ID=%d", actualID.obmcID.id));
253 
254     if (fs::exists(pel->second.path))
255     {
256         // Check for existense of new archive folder
257         if (!fs::exists(_archivePath))
258         {
259             fs::create_directories(_archivePath);
260         }
261 
262         // Move log file to archive folder
263         auto fileName = _archivePath / pel->second.path.filename();
264         fs::rename(pel->second.path, fileName);
265 
266         // Update size of file
267         _archiveSize += getFileDiskSize(fileName);
268     }
269 
270     _pelAttributes.erase(pel);
271 
272     processDeleteCallbacks(actualID.pelID.id);
273 
274     return actualID;
275 }
276 
277 std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id)
278 {
279     auto pel = findPEL(id);
280     if (pel != _pelAttributes.end())
281     {
282         std::ifstream file{pel->second.path.c_str()};
283         if (!file.good())
284         {
285             auto e = errno;
286             log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e),
287                             entry("PATH=%s", pel->second.path.c_str()));
288             throw file_error::Open();
289         }
290 
291         std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
292                                   std::istreambuf_iterator<char>()};
293         return data;
294     }
295 
296     return std::nullopt;
297 }
298 
299 std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id)
300 {
301     auto pel = findPEL(id);
302     if (pel != _pelAttributes.end())
303     {
304         int fd = open(pel->second.path.c_str(), O_RDONLY | O_NONBLOCK);
305         if (fd == -1)
306         {
307             auto e = errno;
308             log<level::ERR>("Unable to open PEL File", entry("ERRNO=%d", e),
309                             entry("PATH=%s", pel->second.path.c_str()));
310             throw file_error::Open();
311         }
312 
313         // Must leave the file open here.  It will be closed by sdbusplus
314         // when it sends it back over D-Bus.
315         return fd;
316     }
317     return std::nullopt;
318 }
319 
320 void Repository::for_each(ForEachFunc func) const
321 {
322     for (const auto& [id, attributes] : _pelAttributes)
323     {
324         std::ifstream file{attributes.path};
325 
326         if (!file.good())
327         {
328             auto e = errno;
329             log<level::ERR>("Repository::for_each: Unable to open PEL file",
330                             entry("ERRNO=%d", e),
331                             entry("PATH=%s", attributes.path.c_str()));
332             continue;
333         }
334 
335         std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
336                                   std::istreambuf_iterator<char>()};
337         file.close();
338 
339         PEL pel{data};
340 
341         try
342         {
343             if (func(pel))
344             {
345                 break;
346             }
347         }
348         catch (const std::exception& e)
349         {
350             log<level::ERR>("Repository::for_each function exception",
351                             entry("ERROR=%s", e.what()));
352         }
353     }
354 }
355 
356 void Repository::processAddCallbacks(const PEL& pel) const
357 {
358     for (auto& [name, func] : _addSubscriptions)
359     {
360         try
361         {
362             func(pel);
363         }
364         catch (const std::exception& e)
365         {
366             log<level::ERR>("PEL Repository add callback exception",
367                             entry("NAME=%s", name.c_str()),
368                             entry("ERROR=%s", e.what()));
369         }
370     }
371 }
372 
373 void Repository::processDeleteCallbacks(uint32_t id) const
374 {
375     for (auto& [name, func] : _deleteSubscriptions)
376     {
377         try
378         {
379             func(id);
380         }
381         catch (const std::exception& e)
382         {
383             log<level::ERR>("PEL Repository delete callback exception",
384                             entry("NAME=%s", name.c_str()),
385                             entry("ERROR=%s", e.what()));
386         }
387     }
388 }
389 
390 std::optional<std::reference_wrapper<const Repository::PELAttributes>>
391     Repository::getPELAttributes(const LogID& id) const
392 {
393     auto pel = findPEL(id);
394     if (pel != _pelAttributes.end())
395     {
396         return pel->second;
397     }
398 
399     return std::nullopt;
400 }
401 
402 void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state)
403 {
404     LogID id{LogID::Pel{pelID}};
405     auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
406                              [&id](const auto& a) { return a.first == id; });
407 
408     if ((attr != _pelAttributes.end()) && (attr->second.hostState != state))
409     {
410         PELUpdateFunc func = [state](PEL& pel) {
411             pel.setHostTransmissionState(state);
412             return true;
413         };
414 
415         try
416         {
417             updatePEL(attr->second.path, func);
418         }
419         catch (const std::exception& e)
420         {
421             log<level::ERR>("Unable to update PEL host transmission state",
422                             entry("PATH=%s", attr->second.path.c_str()),
423                             entry("ERROR=%s", e.what()));
424         }
425     }
426 }
427 
428 void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state)
429 {
430     LogID id{LogID::Pel{pelID}};
431     auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
432                              [&id](const auto& a) { return a.first == id; });
433 
434     if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state))
435     {
436         PELUpdateFunc func = [state](PEL& pel) {
437             pel.setHMCTransmissionState(state);
438             return true;
439         };
440 
441         try
442         {
443             updatePEL(attr->second.path, func);
444         }
445         catch (const std::exception& e)
446         {
447             log<level::ERR>("Unable to update PEL HMC transmission state",
448                             entry("PATH=%s", attr->second.path.c_str()),
449                             entry("ERROR=%s", e.what()));
450         }
451     }
452 }
453 
454 void Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc)
455 {
456     std::ifstream file{path};
457     std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
458                               std::istreambuf_iterator<char>()};
459     file.close();
460 
461     PEL pel{data};
462 
463     if (pel.valid())
464     {
465         if (updateFunc(pel))
466         {
467             // Three attribute fields can change post creation from
468             // an updatePEL call:
469             //  - hmcTransmissionState - When HMC acks a PEL
470             //  - hostTransmissionState - When host acks a PEL
471             //  - deconfig flag - Can be cleared for PELs that call out
472             //                    hotplugged FRUs.
473             // Make sure they're up to date.
474             LogID id{LogID::Pel(pel.id())};
475             auto attr =
476                 std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
477                              [&id](const auto& a) { return a.first == id; });
478             if (attr != _pelAttributes.end())
479             {
480                 attr->second.hmcState = pel.hmcTransmissionState();
481                 attr->second.hostState = pel.hostTransmissionState();
482                 attr->second.deconfig = pel.getDeconfigFlag();
483             }
484 
485             write(pel, path);
486         }
487     }
488     else
489     {
490         throw std::runtime_error(
491             "Unable to read a valid PEL when trying to update it");
492     }
493 }
494 
495 bool Repository::isServiceableSev(const PELAttributes& pel)
496 {
497     auto sevType = static_cast<SeverityType>(pel.severity & 0xF0);
498     auto sevPVEntry = pel_values::findByValue(pel.severity,
499                                               pel_values::severityValues);
500     std::string sevName = std::get<pel_values::registryNamePos>(*sevPVEntry);
501 
502     bool check1 = (sevType == SeverityType::predictive) ||
503                   (sevType == SeverityType::unrecoverable) ||
504                   (sevType == SeverityType::critical);
505 
506     bool check2 = ((sevType == SeverityType::recovered) ||
507                    (sevName == "symptom_recovered")) &&
508                   !pel.actionFlags.test(hiddenFlagBit);
509 
510     bool check3 = (sevName == "symptom_predictive") ||
511                   (sevName == "symptom_unrecoverable") ||
512                   (sevName == "symptom_critical");
513 
514     return check1 || check2 || check3;
515 }
516 
517 void Repository::updateRepoStats(const PELAttributes& pel, bool pelAdded)
518 {
519     auto isServiceable = Repository::isServiceableSev(pel);
520     auto bmcPEL = CreatorID::openBMC == static_cast<CreatorID>(pel.creator);
521 
522     auto adjustSize = [pelAdded, &pel](auto& runningSize) {
523         if (pelAdded)
524         {
525             runningSize += pel.sizeOnDisk;
526         }
527         else
528         {
529             runningSize = std::max(static_cast<int64_t>(runningSize) -
530                                        static_cast<int64_t>(pel.sizeOnDisk),
531                                    static_cast<int64_t>(0));
532         }
533     };
534 
535     adjustSize(_sizes.total);
536 
537     if (bmcPEL)
538     {
539         adjustSize(_sizes.bmc);
540         if (isServiceable)
541         {
542             adjustSize(_sizes.bmcServiceable);
543         }
544         else
545         {
546             adjustSize(_sizes.bmcInfo);
547         }
548     }
549     else
550     {
551         adjustSize(_sizes.nonBMC);
552         if (isServiceable)
553         {
554             adjustSize(_sizes.nonBMCServiceable);
555         }
556         else
557         {
558             adjustSize(_sizes.nonBMCInfo);
559         }
560     }
561 }
562 
563 bool Repository::sizeWarning()
564 {
565     std::error_code ec;
566 
567     if ((_archiveSize > 0) && ((_sizes.total + _archiveSize) >
568                                ((_maxRepoSize * warningPercentage) / 100)))
569     {
570         log<level::INFO>(
571             "Repository::sizeWarning function:Deleting the files in archive");
572 
573         for (const auto& dirEntry : fs::directory_iterator(_archivePath))
574         {
575             fs::remove(dirEntry.path(), ec);
576             if (ec)
577             {
578                 log<level::INFO>(
579                     "Repository::sizeWarning function:Could not delete "
580                     "a file in PEL archive",
581                     entry("FILENAME=%s", dirEntry.path().c_str()));
582             }
583         }
584 
585         _archiveSize = 0;
586     }
587 
588     return (_sizes.total > (_maxRepoSize * warningPercentage / 100)) ||
589            (_pelAttributes.size() > _maxNumPELs);
590 }
591 
592 std::vector<Repository::AttributesReference>
593     Repository::getAllPELAttributes(SortOrder order) const
594 {
595     std::vector<Repository::AttributesReference> attributes;
596 
597     std::for_each(_pelAttributes.begin(), _pelAttributes.end(),
598                   [&attributes](auto& pelEntry) {
599         attributes.push_back(pelEntry);
600     });
601 
602     std::sort(attributes.begin(), attributes.end(),
603               [order](const auto& left, const auto& right) {
604         if (order == SortOrder::ascending)
605         {
606             return left.get().second.path < right.get().second.path;
607         }
608         return left.get().second.path > right.get().second.path;
609     });
610 
611     return attributes;
612 }
613 
614 std::vector<uint32_t>
615     Repository::prune(const std::vector<uint32_t>& idsWithHwIsoEntry)
616 {
617     std::vector<uint32_t> obmcLogIDs;
618     std::string msg = "Pruning PEL repository that takes up " +
619                       std::to_string(_sizes.total) + " bytes and has " +
620                       std::to_string(_pelAttributes.size()) + " PELs";
621     log<level::INFO>(msg.c_str());
622 
623     // Set up the 5 functions to check if the PEL category
624     // is still over its limits.
625 
626     // BMC informational PELs should only take up 15%
627     IsOverLimitFunc overBMCInfoLimit = [this]() {
628         return _sizes.bmcInfo > _maxRepoSize * 15 / 100;
629     };
630 
631     // BMC non informational PELs should only take up 30%
632     IsOverLimitFunc overBMCNonInfoLimit = [this]() {
633         return _sizes.bmcServiceable > _maxRepoSize * 30 / 100;
634     };
635 
636     // Non BMC informational PELs should only take up 15%
637     IsOverLimitFunc overNonBMCInfoLimit = [this]() {
638         return _sizes.nonBMCInfo > _maxRepoSize * 15 / 100;
639     };
640 
641     // Non BMC non informational PELs should only take up 15%
642     IsOverLimitFunc overNonBMCNonInfoLimit = [this]() {
643         return _sizes.nonBMCServiceable > _maxRepoSize * 30 / 100;
644     };
645 
646     // Bring the total number of PELs down to 80% of the max
647     IsOverLimitFunc tooManyPELsLimit = [this]() {
648         return _pelAttributes.size() > _maxNumPELs * 80 / 100;
649     };
650 
651     // Set up the functions to determine which category a PEL is in.
652     // TODO: Return false in these functions if a PEL caused a guard record.
653 
654     // A BMC informational PEL
655     IsPELTypeFunc isBMCInfo = [](const PELAttributes& pel) {
656         return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) &&
657                !Repository::isServiceableSev(pel);
658     };
659 
660     // A BMC non informational PEL
661     IsPELTypeFunc isBMCNonInfo = [](const PELAttributes& pel) {
662         return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) &&
663                Repository::isServiceableSev(pel);
664     };
665 
666     // A non BMC informational PEL
667     IsPELTypeFunc isNonBMCInfo = [](const PELAttributes& pel) {
668         return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) &&
669                !Repository::isServiceableSev(pel);
670     };
671 
672     // A non BMC non informational PEL
673     IsPELTypeFunc isNonBMCNonInfo = [](const PELAttributes& pel) {
674         return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) &&
675                Repository::isServiceableSev(pel);
676     };
677 
678     // When counting PELs, count every PEL
679     IsPELTypeFunc isAnyPEL = [](const PELAttributes& /*pel*/) { return true; };
680 
681     // Check all 4 categories, which will result in at most 90%
682     // usage (15 + 30 + 15 + 30).
683     removePELs(overBMCInfoLimit, isBMCInfo, idsWithHwIsoEntry, obmcLogIDs);
684     removePELs(overBMCNonInfoLimit, isBMCNonInfo, idsWithHwIsoEntry,
685                obmcLogIDs);
686     removePELs(overNonBMCInfoLimit, isNonBMCInfo, idsWithHwIsoEntry,
687                obmcLogIDs);
688     removePELs(overNonBMCNonInfoLimit, isNonBMCNonInfo, idsWithHwIsoEntry,
689                obmcLogIDs);
690 
691     // After the above pruning check if there are still too many PELs,
692     // which can happen depending on PEL sizes.
693     if (_pelAttributes.size() > _maxNumPELs)
694     {
695         removePELs(tooManyPELsLimit, isAnyPEL, idsWithHwIsoEntry, obmcLogIDs);
696     }
697 
698     if (!obmcLogIDs.empty())
699     {
700         std::string m = "Number of PELs removed to save space: " +
701                         std::to_string(obmcLogIDs.size());
702         log<level::INFO>(m.c_str());
703     }
704 
705     return obmcLogIDs;
706 }
707 
708 void Repository::removePELs(const IsOverLimitFunc& isOverLimit,
709                             const IsPELTypeFunc& isPELType,
710                             const std::vector<uint32_t>& idsWithHwIsoEntry,
711                             std::vector<uint32_t>& removedBMCLogIDs)
712 {
713     if (!isOverLimit())
714     {
715         return;
716     }
717 
718     auto attributes = getAllPELAttributes(SortOrder::ascending);
719 
720     // Make 4 passes on the PELs, stopping as soon as isOverLimit
721     // returns false.
722     //   Pass 1: only delete HMC acked PELs
723     //   Pass 2: only delete OS acked PELs
724     //   Pass 3: only delete PHYP sent PELs
725     //   Pass 4: delete all PELs
726     static const std::vector<std::function<bool(const PELAttributes& pel)>>
727         stateChecks{[](const auto& pel) {
728         return pel.hmcState == TransmissionState::acked;
729                     },
730 
731                     [](const auto& pel) {
732         return pel.hostState == TransmissionState::acked;
733         },
734 
735         [](const auto& pel) {
736         return pel.hostState == TransmissionState::sent;
737         },
738 
739         [](const auto& /*pel*/) { return true; }};
740 
741     for (const auto& stateCheck : stateChecks)
742     {
743         for (auto it = attributes.begin(); it != attributes.end();)
744         {
745             const auto& pel = it->get();
746             if (isPELType(pel.second) && stateCheck(pel.second))
747             {
748                 auto removedID = pel.first.obmcID.id;
749 
750                 auto idFound = std::find(idsWithHwIsoEntry.begin(),
751                                          idsWithHwIsoEntry.end(), removedID);
752                 if (idFound != idsWithHwIsoEntry.end())
753                 {
754                     ++it;
755                     continue;
756                 }
757 
758                 remove(pel.first);
759 
760                 removedBMCLogIDs.push_back(removedID);
761 
762                 attributes.erase(it);
763 
764                 if (!isOverLimit())
765                 {
766                     break;
767                 }
768             }
769             else
770             {
771                 ++it;
772             }
773         }
774 
775         if (!isOverLimit())
776         {
777             break;
778         }
779     }
780 }
781 
782 void Repository::archivePEL(const PEL& pel)
783 {
784     if (pel.valid())
785     {
786         auto path = _archivePath / getPELFilename(pel.id(), pel.commitTime());
787 
788         write(pel, path);
789 
790         _archiveSize += getFileDiskSize(path);
791     }
792 }
793 
794 } // namespace pels
795 } // namespace openpower
796