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