blob: c49d92f01e679d87e1ca35069b5257e0be4d0c5e [file] [log] [blame]
Matt Spinler711d51d2019-11-06 09:36:51 -06001/**
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 */
Matt Spinler89fa0822019-07-17 13:54:30 -050016#include "repository.hpp"
17
Matt Spinlerdd325c32020-07-07 11:01:54 -050018#include <sys/stat.h>
19
Matt Spinler89fa0822019-07-17 13:54:30 -050020#include <fstream>
21#include <phosphor-logging/log.hpp>
22#include <xyz/openbmc_project/Common/File/error.hpp>
23
24namespace openpower
25{
26namespace pels
27{
28
29namespace fs = std::filesystem;
30using namespace phosphor::logging;
31namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
32
Matt Spinler7e727a32020-07-07 15:00:17 -050033constexpr size_t warningPercentage = 95;
34
Matt Spinlerdd325c32020-07-07 11:01:54 -050035/**
36 * @brief Returns the amount of space the file uses on disk.
37 *
38 * This is different than just the regular size of the file.
39 *
40 * @param[in] file - The file to get the size of
41 *
42 * @return size_t The disk space the file uses
43 */
44size_t getFileDiskSize(const std::filesystem::path& file)
45{
46 constexpr size_t statBlockSize = 512;
47 struct stat statData;
48 auto rc = stat(file.c_str(), &statData);
49 if (rc != 0)
50 {
51 auto e = errno;
52 std::string msg = "call to stat() failed on " + file.native() +
53 " with errno " + std::to_string(e);
54 log<level::ERR>(msg.c_str());
55 abort();
56 }
57
58 return statData.st_blocks * statBlockSize;
59}
60
Matt Spinler8d5f3a22020-07-07 10:30:33 -050061Repository::Repository(const std::filesystem::path& basePath, size_t repoSize,
62 size_t maxNumPELs) :
63 _logPath(basePath / "logs"),
Sumit Kumar1d8835b2021-06-07 09:35:30 -050064 _maxRepoSize(repoSize), _maxNumPELs(maxNumPELs),
65 _archivePath(basePath / "logs" / "archive")
Matt Spinler89fa0822019-07-17 13:54:30 -050066{
67 if (!fs::exists(_logPath))
68 {
69 fs::create_directories(_logPath);
70 }
Matt Spinler475e5742019-07-18 16:09:49 -050071
Sumit Kumar1d8835b2021-06-07 09:35:30 -050072 if (!fs::exists(_archivePath))
73 {
74 fs::create_directories(_archivePath);
75 }
76
Matt Spinler475e5742019-07-18 16:09:49 -050077 restore();
78}
79
80void Repository::restore()
81{
82 for (auto& dirEntry : fs::directory_iterator(_logPath))
83 {
84 try
85 {
86 if (!fs::is_regular_file(dirEntry.path()))
87 {
88 continue;
89 }
90
91 std::ifstream file{dirEntry.path()};
92 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
93 std::istreambuf_iterator<char>()};
94 file.close();
95
Matt Spinler07eefc52019-09-26 11:18:26 -050096 PEL pel{data};
Matt Spinler475e5742019-07-18 16:09:49 -050097 if (pel.valid())
98 {
Matt Spinlera3c12a42019-11-21 13:25:32 -060099 // If the host hasn't acked it, reset the host state so
100 // it will get sent up again.
101 if (pel.hostTransmissionState() == TransmissionState::sent)
102 {
103 pel.setHostTransmissionState(TransmissionState::newPEL);
104 try
105 {
106 write(pel, dirEntry.path());
107 }
108 catch (std::exception& e)
109 {
110 log<level::ERR>(
111 "Failed to save PEL after updating host state",
112 entry("PELID=0x%X", pel.id()));
113 }
114 }
115
Matt Spinlerdd325c32020-07-07 11:01:54 -0500116 PELAttributes attributes{dirEntry.path(),
117 getFileDiskSize(dirEntry.path()),
118 pel.privateHeader().creatorID(),
119 pel.userHeader().severity(),
120 pel.userHeader().actionFlags(),
121 pel.hostTransmissionState(),
122 pel.hmcTransmissionState()};
Matt Spinler0ff00482019-11-06 16:19:46 -0600123
Matt Spinler475e5742019-07-18 16:09:49 -0500124 using pelID = LogID::Pel;
125 using obmcID = LogID::Obmc;
Matt Spinler0ff00482019-11-06 16:19:46 -0600126 _pelAttributes.emplace(
Matt Spinler475e5742019-07-18 16:09:49 -0500127 LogID(pelID(pel.id()), obmcID(pel.obmcLogID())),
Matt Spinler0ff00482019-11-06 16:19:46 -0600128 attributes);
Matt Spinlerb188f782020-07-07 11:18:12 -0500129
130 updateRepoStats(attributes, true);
Matt Spinler475e5742019-07-18 16:09:49 -0500131 }
132 else
133 {
134 log<level::ERR>(
135 "Found invalid PEL file while restoring. Removing.",
136 entry("FILENAME=%s", dirEntry.path().c_str()));
137 fs::remove(dirEntry.path());
138 }
139 }
140 catch (std::exception& e)
141 {
142 log<level::ERR>("Hit exception while restoring PEL File",
143 entry("FILENAME=%s", dirEntry.path().c_str()),
144 entry("ERROR=%s", e.what()));
145 }
146 }
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500147
148 // Get size of archive folder
149 for (auto& dirEntry : fs::directory_iterator(_archivePath))
150 {
151 _archiveSize += getFileDiskSize(dirEntry);
152 }
Matt Spinler89fa0822019-07-17 13:54:30 -0500153}
154
155std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time)
156{
157 char name[50];
158 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB,
159 time.yearLSB, time.month, time.day, time.hour, time.minutes,
160 time.seconds, time.hundredths, pelID);
161 return std::string{name};
162}
163
164void Repository::add(std::unique_ptr<PEL>& pel)
165{
Matt Spinlerdf43a302019-11-21 13:16:56 -0600166 pel->setHostTransmissionState(TransmissionState::newPEL);
167 pel->setHMCTransmissionState(TransmissionState::newPEL);
168
Matt Spinler89fa0822019-07-17 13:54:30 -0500169 auto path = _logPath / getPELFilename(pel->id(), pel->commitTime());
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600170
171 write(*(pel.get()), path);
172
Matt Spinlerdd325c32020-07-07 11:01:54 -0500173 PELAttributes attributes{path,
174 getFileDiskSize(path),
175 pel->privateHeader().creatorID(),
176 pel->userHeader().severity(),
177 pel->userHeader().actionFlags(),
Matt Spinler346f99a2019-11-21 13:06:35 -0600178 pel->hostTransmissionState(),
179 pel->hmcTransmissionState()};
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600180
181 using pelID = LogID::Pel;
182 using obmcID = LogID::Obmc;
183 _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())),
184 attributes);
185
Matt Spinler44893cc2020-08-26 11:34:17 -0500186 _lastPelID = pel->id();
187
Matt Spinlerb188f782020-07-07 11:18:12 -0500188 updateRepoStats(attributes, true);
189
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600190 processAddCallbacks(*pel);
191}
192
193void Repository::write(const PEL& pel, const fs::path& path)
194{
Matt Spinler89fa0822019-07-17 13:54:30 -0500195 std::ofstream file{path, std::ios::binary};
196
197 if (!file.good())
198 {
199 // If this fails, the filesystem is probably full so it isn't like
200 // we could successfully create yet another error log here.
201 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500202 fs::remove(path);
203 log<level::ERR>("Unable to open PEL file for writing",
204 entry("ERRNO=%d", e), entry("PATH=%s", path.c_str()));
205 throw file_error::Open();
206 }
207
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600208 auto data = pel.data();
Matt Spinler89fa0822019-07-17 13:54:30 -0500209 file.write(reinterpret_cast<const char*>(data.data()), data.size());
210
211 if (file.fail())
212 {
213 // Same note as above about not being able to create an error log
214 // for this case even if we wanted.
215 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500216 file.close();
217 fs::remove(path);
218 log<level::ERR>("Unable to write PEL file", entry("ERRNO=%d", e),
219 entry("PATH=%s", path.c_str()));
220 throw file_error::Write();
221 }
Matt Spinler475e5742019-07-18 16:09:49 -0500222}
223
Matt Spinler52602e32020-07-15 12:37:28 -0500224std::optional<Repository::LogID> Repository::remove(const LogID& id)
Matt Spinler475e5742019-07-18 16:09:49 -0500225{
226 auto pel = findPEL(id);
Patrick Williamsff6b5982021-04-22 09:04:17 -0500227 if (pel == _pelAttributes.end())
Matt Spinler475e5742019-07-18 16:09:49 -0500228 {
Patrick Williamsff6b5982021-04-22 09:04:17 -0500229 return std::nullopt;
Matt Spinler5f5352e2020-03-05 16:23:27 -0600230 }
Matt Spinler52602e32020-07-15 12:37:28 -0500231
Patrick Williamsff6b5982021-04-22 09:04:17 -0500232 LogID actualID = pel->first;
233 updateRepoStats(pel->second, false);
234
235 log<level::DEBUG>("Removing PEL from repository",
236 entry("PEL_ID=0x%X", actualID.pelID.id),
237 entry("OBMC_LOG_ID=%d", actualID.obmcID.id));
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500238
239 if (fs::exists(pel->second.path))
240 {
241 // Check for existense of new archive folder
242 if (!fs::exists(_archivePath))
243 {
244 fs::create_directories(_archivePath);
245 }
246
247 // Move log file to archive folder
248 auto fileName = _archivePath / pel->second.path.filename();
249 fs::rename(pel->second.path, fileName);
250
251 // Update size of file
252 _archiveSize += getFileDiskSize(fileName);
253 }
254
Patrick Williamsff6b5982021-04-22 09:04:17 -0500255 _pelAttributes.erase(pel);
256
257 processDeleteCallbacks(actualID.pelID.id);
258
Matt Spinler52602e32020-07-15 12:37:28 -0500259 return actualID;
Matt Spinler89fa0822019-07-17 13:54:30 -0500260}
261
Matt Spinler2813f362019-07-19 12:45:28 -0500262std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id)
263{
264 auto pel = findPEL(id);
Matt Spinler0ff00482019-11-06 16:19:46 -0600265 if (pel != _pelAttributes.end())
Matt Spinler2813f362019-07-19 12:45:28 -0500266 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600267 std::ifstream file{pel->second.path.c_str()};
Matt Spinler2813f362019-07-19 12:45:28 -0500268 if (!file.good())
269 {
270 auto e = errno;
271 log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e),
Matt Spinler0ff00482019-11-06 16:19:46 -0600272 entry("PATH=%s", pel->second.path.c_str()));
Matt Spinler2813f362019-07-19 12:45:28 -0500273 throw file_error::Open();
274 }
275
276 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
277 std::istreambuf_iterator<char>()};
278 return data;
279 }
280
281 return std::nullopt;
282}
283
Matt Spinler6d512242019-12-09 13:44:17 -0600284std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id)
285{
286 auto pel = findPEL(id);
287 if (pel != _pelAttributes.end())
288 {
289 FILE* fp = fopen(pel->second.path.c_str(), "rb");
290
291 if (fp == nullptr)
292 {
293 auto e = errno;
294 log<level::ERR>("Unable to open PEL File", entry("ERRNO=%d", e),
295 entry("PATH=%s", pel->second.path.c_str()));
296 throw file_error::Open();
297 }
298
299 // Must leave the file open here. It will be closed by sdbusplus
300 // when it sends it back over D-Bus.
301
302 return fileno(fp);
303 }
304 return std::nullopt;
305}
306
Matt Spinler1ea78802019-11-01 13:04:59 -0500307void Repository::for_each(ForEachFunc func) const
308{
Matt Spinler0ff00482019-11-06 16:19:46 -0600309 for (const auto& [id, attributes] : _pelAttributes)
Matt Spinler1ea78802019-11-01 13:04:59 -0500310 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600311 std::ifstream file{attributes.path};
Matt Spinler1ea78802019-11-01 13:04:59 -0500312
313 if (!file.good())
314 {
315 auto e = errno;
316 log<level::ERR>("Repository::for_each: Unable to open PEL file",
317 entry("ERRNO=%d", e),
Matt Spinler0ff00482019-11-06 16:19:46 -0600318 entry("PATH=%s", attributes.path.c_str()));
Matt Spinler1ea78802019-11-01 13:04:59 -0500319 continue;
320 }
321
322 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
323 std::istreambuf_iterator<char>()};
324 file.close();
325
326 PEL pel{data};
327
328 try
329 {
330 if (func(pel))
331 {
332 break;
333 }
334 }
335 catch (std::exception& e)
336 {
337 log<level::ERR>("Repository::for_each function exception",
338 entry("ERROR=%s", e.what()));
339 }
340 }
341}
342
Matt Spinler421f6532019-11-06 15:40:45 -0600343void Repository::processAddCallbacks(const PEL& pel) const
344{
345 for (auto& [name, func] : _addSubscriptions)
346 {
347 try
348 {
349 func(pel);
350 }
351 catch (std::exception& e)
352 {
353 log<level::ERR>("PEL Repository add callback exception",
354 entry("NAME=%s", name.c_str()),
355 entry("ERROR=%s", e.what()));
356 }
357 }
358}
359
360void Repository::processDeleteCallbacks(uint32_t id) const
361{
362 for (auto& [name, func] : _deleteSubscriptions)
363 {
364 try
365 {
366 func(id);
367 }
368 catch (std::exception& e)
369 {
370 log<level::ERR>("PEL Repository delete callback exception",
371 entry("NAME=%s", name.c_str()),
372 entry("ERROR=%s", e.what()));
373 }
374 }
375}
376
Matt Spinler0ff00482019-11-06 16:19:46 -0600377std::optional<std::reference_wrapper<const Repository::PELAttributes>>
378 Repository::getPELAttributes(const LogID& id) const
379{
380 auto pel = findPEL(id);
381 if (pel != _pelAttributes.end())
382 {
383 return pel->second;
384 }
385
386 return std::nullopt;
387}
388
Matt Spinler29d18c12019-11-21 13:31:27 -0600389void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state)
390{
391 LogID id{LogID::Pel{pelID}};
392 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
393 [&id](const auto& a) { return a.first == id; });
394
395 if ((attr != _pelAttributes.end()) && (attr->second.hostState != state))
396 {
397 PELUpdateFunc func = [state](PEL& pel) {
398 pel.setHostTransmissionState(state);
399 };
400
401 try
402 {
403 updatePEL(attr->second.path, func);
404
405 attr->second.hostState = state;
406 }
407 catch (std::exception& e)
408 {
409 log<level::ERR>("Unable to update PEL host transmission state",
410 entry("PATH=%s", attr->second.path.c_str()),
411 entry("ERROR=%s", e.what()));
412 }
413 }
414}
415
416void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state)
417{
418 LogID id{LogID::Pel{pelID}};
419 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
420 [&id](const auto& a) { return a.first == id; });
421
422 if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state))
423 {
424 PELUpdateFunc func = [state](PEL& pel) {
425 pel.setHMCTransmissionState(state);
426 };
427
428 try
429 {
430 updatePEL(attr->second.path, func);
431
432 attr->second.hmcState = state;
433 }
434 catch (std::exception& e)
435 {
436 log<level::ERR>("Unable to update PEL HMC transmission state",
437 entry("PATH=%s", attr->second.path.c_str()),
438 entry("ERROR=%s", e.what()));
439 }
440 }
441}
442
443void Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc)
444{
445 std::ifstream file{path};
446 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
447 std::istreambuf_iterator<char>()};
448 file.close();
449
450 PEL pel{data};
451
452 if (pel.valid())
453 {
454 updateFunc(pel);
455
456 write(pel, path);
457 }
458 else
459 {
460 throw std::runtime_error(
461 "Unable to read a valid PEL when trying to update it");
462 }
463}
464
Matt Spinlerb188f782020-07-07 11:18:12 -0500465bool Repository::isServiceableSev(const PELAttributes& pel)
466{
467 auto sevType = static_cast<SeverityType>(pel.severity & 0xF0);
468 auto sevPVEntry =
469 pel_values::findByValue(pel.severity, pel_values::severityValues);
470 std::string sevName = std::get<pel_values::registryNamePos>(*sevPVEntry);
471
472 bool check1 = (sevType == SeverityType::predictive) ||
473 (sevType == SeverityType::unrecoverable) ||
474 (sevType == SeverityType::critical);
475
476 bool check2 = ((sevType == SeverityType::recovered) ||
477 (sevName == "symptom_recovered")) &&
478 !pel.actionFlags.test(hiddenFlagBit);
479
480 bool check3 = (sevName == "symptom_predictive") ||
481 (sevName == "symptom_unrecoverable") ||
482 (sevName == "symptom_critical");
483
484 return check1 || check2 || check3;
485}
486
487void Repository::updateRepoStats(const PELAttributes& pel, bool pelAdded)
488{
489 auto isServiceable = Repository::isServiceableSev(pel);
490 auto bmcPEL = CreatorID::openBMC == static_cast<CreatorID>(pel.creator);
491
492 auto adjustSize = [pelAdded, &pel](auto& runningSize) {
493 if (pelAdded)
494 {
495 runningSize += pel.sizeOnDisk;
496 }
497 else
498 {
499 runningSize = std::max(static_cast<int64_t>(runningSize) -
500 static_cast<int64_t>(pel.sizeOnDisk),
501 static_cast<int64_t>(0));
502 }
503 };
504
505 adjustSize(_sizes.total);
506
507 if (bmcPEL)
508 {
509 adjustSize(_sizes.bmc);
510 if (isServiceable)
511 {
512 adjustSize(_sizes.bmcServiceable);
513 }
514 else
515 {
516 adjustSize(_sizes.bmcInfo);
517 }
518 }
519 else
520 {
521 adjustSize(_sizes.nonBMC);
522 if (isServiceable)
523 {
524 adjustSize(_sizes.nonBMCServiceable);
525 }
526 else
527 {
528 adjustSize(_sizes.nonBMCInfo);
529 }
530 }
531}
532
Sumit Kumarc2966922021-07-21 10:14:03 -0500533bool Repository::sizeWarning()
Matt Spinler7e727a32020-07-07 15:00:17 -0500534{
Sumit Kumarc2966922021-07-21 10:14:03 -0500535 std::error_code ec;
536
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500537 if ((_archiveSize > 0) && ((_sizes.total + _archiveSize) >
538 ((_maxRepoSize * warningPercentage) / 100)))
539 {
540 log<level::INFO>(
541 "Repository::sizeWarning function:Deleting the files in archive");
542
Sumit Kumarc2966922021-07-21 10:14:03 -0500543 for (const auto& dirEntry : fs::directory_iterator(_archivePath))
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500544 {
Sumit Kumarc2966922021-07-21 10:14:03 -0500545 fs::remove(dirEntry.path(), ec);
546 if (ec)
547 {
548 log<level::INFO>(
549 "Repository::sizeWarning function:Could not delete "
550 "a file in PEL archive",
551 entry("FILENAME=%s", dirEntry.path().c_str()));
552 }
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500553 }
Sumit Kumarc2966922021-07-21 10:14:03 -0500554
555 _archiveSize = 0;
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500556 }
557
Matt Spinler7e727a32020-07-07 15:00:17 -0500558 return (_sizes.total > (_maxRepoSize * warningPercentage / 100)) ||
559 (_pelAttributes.size() > _maxNumPELs);
560}
561
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500562std::vector<Repository::AttributesReference>
563 Repository::getAllPELAttributes(SortOrder order) const
564{
565 std::vector<Repository::AttributesReference> attributes;
566
567 std::for_each(
568 _pelAttributes.begin(), _pelAttributes.end(),
569 [&attributes](auto& pelEntry) { attributes.push_back(pelEntry); });
570
571 std::sort(attributes.begin(), attributes.end(),
572 [order](const auto& left, const auto& right) {
573 if (order == SortOrder::ascending)
574 {
575 return left.get().second.path < right.get().second.path;
576 }
577 return left.get().second.path > right.get().second.path;
578 });
579
580 return attributes;
581}
582
583std::vector<uint32_t> Repository::prune()
584{
585 std::vector<uint32_t> obmcLogIDs;
586 std::string msg = "Pruning PEL repository that takes up " +
587 std::to_string(_sizes.total) + " bytes and has " +
588 std::to_string(_pelAttributes.size()) + " PELs";
589 log<level::INFO>(msg.c_str());
590
591 // Set up the 5 functions to check if the PEL category
592 // is still over its limits.
593
594 // BMC informational PELs should only take up 15%
595 IsOverLimitFunc overBMCInfoLimit = [this]() {
596 return _sizes.bmcInfo > _maxRepoSize * 15 / 100;
597 };
598
599 // BMC non informational PELs should only take up 30%
600 IsOverLimitFunc overBMCNonInfoLimit = [this]() {
601 return _sizes.bmcServiceable > _maxRepoSize * 30 / 100;
602 };
603
604 // Non BMC informational PELs should only take up 15%
605 IsOverLimitFunc overNonBMCInfoLimit = [this]() {
606 return _sizes.nonBMCInfo > _maxRepoSize * 15 / 100;
607 };
608
609 // Non BMC non informational PELs should only take up 15%
610 IsOverLimitFunc overNonBMCNonInfoLimit = [this]() {
611 return _sizes.nonBMCServiceable > _maxRepoSize * 30 / 100;
612 };
613
614 // Bring the total number of PELs down to 80% of the max
615 IsOverLimitFunc tooManyPELsLimit = [this]() {
616 return _pelAttributes.size() > _maxNumPELs * 80 / 100;
617 };
618
619 // Set up the functions to determine which category a PEL is in.
620 // TODO: Return false in these functions if a PEL caused a guard record.
621
622 // A BMC informational PEL
623 IsPELTypeFunc isBMCInfo = [](const PELAttributes& pel) {
624 return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) &&
625 !Repository::isServiceableSev(pel);
626 };
627
628 // A BMC non informational PEL
629 IsPELTypeFunc isBMCNonInfo = [](const PELAttributes& pel) {
630 return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) &&
631 Repository::isServiceableSev(pel);
632 };
633
634 // A non BMC informational PEL
635 IsPELTypeFunc isNonBMCInfo = [](const PELAttributes& pel) {
636 return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) &&
637 !Repository::isServiceableSev(pel);
638 };
639
640 // A non BMC non informational PEL
641 IsPELTypeFunc isNonBMCNonInfo = [](const PELAttributes& pel) {
642 return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) &&
643 Repository::isServiceableSev(pel);
644 };
645
646 // When counting PELs, count every PEL
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500647 IsPELTypeFunc isAnyPEL = [](const PELAttributes& /*pel*/) { return true; };
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500648
649 // Check all 4 categories, which will result in at most 90%
650 // usage (15 + 30 + 15 + 30).
651 removePELs(overBMCInfoLimit, isBMCInfo, obmcLogIDs);
652 removePELs(overBMCNonInfoLimit, isBMCNonInfo, obmcLogIDs);
653 removePELs(overNonBMCInfoLimit, isNonBMCInfo, obmcLogIDs);
654 removePELs(overNonBMCNonInfoLimit, isNonBMCNonInfo, obmcLogIDs);
655
656 // After the above pruning check if there are still too many PELs,
657 // which can happen depending on PEL sizes.
658 if (_pelAttributes.size() > _maxNumPELs)
659 {
660 removePELs(tooManyPELsLimit, isAnyPEL, obmcLogIDs);
661 }
662
663 if (!obmcLogIDs.empty())
664 {
665 std::string msg = "Number of PELs removed to save space: " +
666 std::to_string(obmcLogIDs.size());
667 log<level::INFO>(msg.c_str());
668 }
669
670 return obmcLogIDs;
671}
672
673void Repository::removePELs(IsOverLimitFunc& isOverLimit,
674 IsPELTypeFunc& isPELType,
675 std::vector<uint32_t>& removedBMCLogIDs)
676{
677 if (!isOverLimit())
678 {
679 return;
680 }
681
682 auto attributes = getAllPELAttributes(SortOrder::ascending);
683
684 // Make 4 passes on the PELs, stopping as soon as isOverLimit
685 // returns false.
686 // Pass 1: only delete HMC acked PELs
687 // Pass 2: only delete OS acked PELs
688 // Pass 3: only delete PHYP sent PELs
689 // Pass 4: delete all PELs
690 static const std::vector<std::function<bool(const PELAttributes& pel)>>
691 stateChecks{[](const auto& pel) {
692 return pel.hmcState == TransmissionState::acked;
693 },
694
695 [](const auto& pel) {
696 return pel.hostState == TransmissionState::acked;
697 },
698
699 [](const auto& pel) {
700 return pel.hostState == TransmissionState::sent;
701 },
702
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500703 [](const auto& /*pel*/) { return true; }};
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500704
705 for (const auto& stateCheck : stateChecks)
706 {
707 for (auto it = attributes.begin(); it != attributes.end();)
708 {
709 const auto& pel = it->get();
710 if (isPELType(pel.second) && stateCheck(pel.second))
711 {
712 auto removedID = pel.first.obmcID.id;
713 remove(pel.first);
714
715 removedBMCLogIDs.push_back(removedID);
716
717 attributes.erase(it);
718
719 if (!isOverLimit())
720 {
721 break;
722 }
723 }
724 else
725 {
726 ++it;
727 }
728 }
729
730 if (!isOverLimit())
731 {
732 break;
733 }
734 }
735}
736
Matt Spinler89fa0822019-07-17 13:54:30 -0500737} // namespace pels
738} // namespace openpower