blob: 9bab95b2c37cff2f2319170f25b46956c5beb52b [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 Spinler32a6df62023-01-12 16:30:40 -060018#include <fcntl.h>
Matt Spinlerdd325c32020-07-07 11:01:54 -050019#include <sys/stat.h>
20
Matt Spinler89fa0822019-07-17 13:54:30 -050021#include <phosphor-logging/log.hpp>
22#include <xyz/openbmc_project/Common/File/error.hpp>
23
Patrick Williams2544b412022-10-04 08:41:06 -050024#include <fstream>
25
Matt Spinler89fa0822019-07-17 13:54:30 -050026namespace openpower
27{
28namespace pels
29{
30
31namespace fs = std::filesystem;
32using namespace phosphor::logging;
33namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
34
Matt Spinler7e727a32020-07-07 15:00:17 -050035constexpr size_t warningPercentage = 95;
36
Matt Spinlerdd325c32020-07-07 11:01:54 -050037/**
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 */
46size_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
Matt Spinler8d5f3a22020-07-07 10:30:33 -050063Repository::Repository(const std::filesystem::path& basePath, size_t repoSize,
64 size_t maxNumPELs) :
65 _logPath(basePath / "logs"),
Sumit Kumar1d8835b2021-06-07 09:35:30 -050066 _maxRepoSize(repoSize), _maxNumPELs(maxNumPELs),
67 _archivePath(basePath / "logs" / "archive")
Matt Spinler89fa0822019-07-17 13:54:30 -050068{
69 if (!fs::exists(_logPath))
70 {
71 fs::create_directories(_logPath);
72 }
Matt Spinler475e5742019-07-18 16:09:49 -050073
Sumit Kumar1d8835b2021-06-07 09:35:30 -050074 if (!fs::exists(_archivePath))
75 {
76 fs::create_directories(_archivePath);
77 }
78
Matt Spinler475e5742019-07-18 16:09:49 -050079 restore();
80}
81
82void 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
Matt Spinler07eefc52019-09-26 11:18:26 -050098 PEL pel{data};
Matt Spinler475e5742019-07-18 16:09:49 -050099 if (pel.valid())
100 {
Matt Spinlera3c12a42019-11-21 13:25:32 -0600101 // 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 }
Patrick Williams66491c62021-10-06 12:23:37 -0500110 catch (const std::exception& e)
Matt Spinlera3c12a42019-11-21 13:25:32 -0600111 {
112 log<level::ERR>(
113 "Failed to save PEL after updating host state",
114 entry("PELID=0x%X", pel.id()));
115 }
116 }
117
Matt Spinler8e65f4e2023-05-02 13:40:08 -0500118 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())};
Matt Spinler0ff00482019-11-06 16:19:46 -0600132
Matt Spinler475e5742019-07-18 16:09:49 -0500133 using pelID = LogID::Pel;
134 using obmcID = LogID::Obmc;
Matt Spinler0ff00482019-11-06 16:19:46 -0600135 _pelAttributes.emplace(
Matt Spinler475e5742019-07-18 16:09:49 -0500136 LogID(pelID(pel.id()), obmcID(pel.obmcLogID())),
Matt Spinler0ff00482019-11-06 16:19:46 -0600137 attributes);
Matt Spinlerb188f782020-07-07 11:18:12 -0500138
139 updateRepoStats(attributes, true);
Matt Spinler475e5742019-07-18 16:09:49 -0500140 }
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 }
Patrick Williams66491c62021-10-06 12:23:37 -0500149 catch (const std::exception& e)
Matt Spinler475e5742019-07-18 16:09:49 -0500150 {
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 }
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500156
157 // Get size of archive folder
158 for (auto& dirEntry : fs::directory_iterator(_archivePath))
159 {
160 _archiveSize += getFileDiskSize(dirEntry);
161 }
Matt Spinler89fa0822019-07-17 13:54:30 -0500162}
163
164std::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
173void Repository::add(std::unique_ptr<PEL>& pel)
174{
Matt Spinlerdf43a302019-11-21 13:16:56 -0600175 pel->setHostTransmissionState(TransmissionState::newPEL);
176 pel->setHMCTransmissionState(TransmissionState::newPEL);
177
Matt Spinler89fa0822019-07-17 13:54:30 -0500178 auto path = _logPath / getPELFilename(pel->id(), pel->commitTime());
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600179
180 write(*(pel.get()), path);
181
Matt Spinler8e65f4e2023-05-02 13:40:08 -0500182 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())};
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600195
196 using pelID = LogID::Pel;
197 using obmcID = LogID::Obmc;
198 _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())),
199 attributes);
200
Matt Spinler44893cc2020-08-26 11:34:17 -0500201 _lastPelID = pel->id();
202
Matt Spinlerb188f782020-07-07 11:18:12 -0500203 updateRepoStats(attributes, true);
204
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600205 processAddCallbacks(*pel);
206}
207
208void Repository::write(const PEL& pel, const fs::path& path)
209{
Matt Spinler89fa0822019-07-17 13:54:30 -0500210 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;
Matt Spinler89fa0822019-07-17 13:54:30 -0500217 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
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600223 auto data = pel.data();
Matt Spinler89fa0822019-07-17 13:54:30 -0500224 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;
Matt Spinler89fa0822019-07-17 13:54:30 -0500231 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 }
Matt Spinler475e5742019-07-18 16:09:49 -0500237}
238
Matt Spinler52602e32020-07-15 12:37:28 -0500239std::optional<Repository::LogID> Repository::remove(const LogID& id)
Matt Spinler475e5742019-07-18 16:09:49 -0500240{
241 auto pel = findPEL(id);
Patrick Williamsff6b5982021-04-22 09:04:17 -0500242 if (pel == _pelAttributes.end())
Matt Spinler475e5742019-07-18 16:09:49 -0500243 {
Patrick Williamsff6b5982021-04-22 09:04:17 -0500244 return std::nullopt;
Matt Spinler5f5352e2020-03-05 16:23:27 -0600245 }
Matt Spinler52602e32020-07-15 12:37:28 -0500246
Patrick Williamsff6b5982021-04-22 09:04:17 -0500247 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));
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500253
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
Patrick Williamsff6b5982021-04-22 09:04:17 -0500270 _pelAttributes.erase(pel);
271
272 processDeleteCallbacks(actualID.pelID.id);
273
Matt Spinler52602e32020-07-15 12:37:28 -0500274 return actualID;
Matt Spinler89fa0822019-07-17 13:54:30 -0500275}
276
Matt Spinler2813f362019-07-19 12:45:28 -0500277std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id)
278{
279 auto pel = findPEL(id);
Matt Spinler0ff00482019-11-06 16:19:46 -0600280 if (pel != _pelAttributes.end())
Matt Spinler2813f362019-07-19 12:45:28 -0500281 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600282 std::ifstream file{pel->second.path.c_str()};
Matt Spinler2813f362019-07-19 12:45:28 -0500283 if (!file.good())
284 {
285 auto e = errno;
286 log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e),
Matt Spinler0ff00482019-11-06 16:19:46 -0600287 entry("PATH=%s", pel->second.path.c_str()));
Matt Spinler2813f362019-07-19 12:45:28 -0500288 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
Matt Spinler6d512242019-12-09 13:44:17 -0600299std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id)
300{
301 auto pel = findPEL(id);
302 if (pel != _pelAttributes.end())
303 {
Matt Spinler32a6df62023-01-12 16:30:40 -0600304 int fd = open(pel->second.path.c_str(), O_RDONLY | O_NONBLOCK);
305 if (fd == -1)
Matt Spinler6d512242019-12-09 13:44:17 -0600306 {
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.
Matt Spinler32a6df62023-01-12 16:30:40 -0600315 return fd;
Matt Spinler6d512242019-12-09 13:44:17 -0600316 }
317 return std::nullopt;
318}
319
Matt Spinler1ea78802019-11-01 13:04:59 -0500320void Repository::for_each(ForEachFunc func) const
321{
Matt Spinler0ff00482019-11-06 16:19:46 -0600322 for (const auto& [id, attributes] : _pelAttributes)
Matt Spinler1ea78802019-11-01 13:04:59 -0500323 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600324 std::ifstream file{attributes.path};
Matt Spinler1ea78802019-11-01 13:04:59 -0500325
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),
Matt Spinler0ff00482019-11-06 16:19:46 -0600331 entry("PATH=%s", attributes.path.c_str()));
Matt Spinler1ea78802019-11-01 13:04:59 -0500332 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 }
Patrick Williams66491c62021-10-06 12:23:37 -0500348 catch (const std::exception& e)
Matt Spinler1ea78802019-11-01 13:04:59 -0500349 {
350 log<level::ERR>("Repository::for_each function exception",
351 entry("ERROR=%s", e.what()));
352 }
353 }
354}
355
Matt Spinler421f6532019-11-06 15:40:45 -0600356void Repository::processAddCallbacks(const PEL& pel) const
357{
358 for (auto& [name, func] : _addSubscriptions)
359 {
360 try
361 {
362 func(pel);
363 }
Patrick Williams66491c62021-10-06 12:23:37 -0500364 catch (const std::exception& e)
Matt Spinler421f6532019-11-06 15:40:45 -0600365 {
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
373void Repository::processDeleteCallbacks(uint32_t id) const
374{
375 for (auto& [name, func] : _deleteSubscriptions)
376 {
377 try
378 {
379 func(id);
380 }
Patrick Williams66491c62021-10-06 12:23:37 -0500381 catch (const std::exception& e)
Matt Spinler421f6532019-11-06 15:40:45 -0600382 {
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
Matt Spinler0ff00482019-11-06 16:19:46 -0600390std::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
Matt Spinler29d18c12019-11-21 13:31:27 -0600402void 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);
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500412 return true;
Matt Spinler29d18c12019-11-21 13:31:27 -0600413 };
414
415 try
416 {
417 updatePEL(attr->second.path, func);
Matt Spinler29d18c12019-11-21 13:31:27 -0600418 }
Patrick Williams66491c62021-10-06 12:23:37 -0500419 catch (const std::exception& e)
Matt Spinler29d18c12019-11-21 13:31:27 -0600420 {
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
428void 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);
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500438 return true;
Matt Spinler29d18c12019-11-21 13:31:27 -0600439 };
440
441 try
442 {
443 updatePEL(attr->second.path, func);
Matt Spinler29d18c12019-11-21 13:31:27 -0600444 }
Patrick Williams66491c62021-10-06 12:23:37 -0500445 catch (const std::exception& e)
Matt Spinler29d18c12019-11-21 13:31:27 -0600446 {
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
454void 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 {
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500465 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 }
Matt Spinler29d18c12019-11-21 13:31:27 -0600484
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500485 write(pel, path);
486 }
Matt Spinler29d18c12019-11-21 13:31:27 -0600487 }
488 else
489 {
490 throw std::runtime_error(
491 "Unable to read a valid PEL when trying to update it");
492 }
493}
494
Matt Spinlerb188f782020-07-07 11:18:12 -0500495bool Repository::isServiceableSev(const PELAttributes& pel)
496{
497 auto sevType = static_cast<SeverityType>(pel.severity & 0xF0);
Patrick Williams2544b412022-10-04 08:41:06 -0500498 auto sevPVEntry = pel_values::findByValue(pel.severity,
499 pel_values::severityValues);
Matt Spinlerb188f782020-07-07 11:18:12 -0500500 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
517void 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
Sumit Kumarc2966922021-07-21 10:14:03 -0500563bool Repository::sizeWarning()
Matt Spinler7e727a32020-07-07 15:00:17 -0500564{
Sumit Kumarc2966922021-07-21 10:14:03 -0500565 std::error_code ec;
566
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500567 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
Sumit Kumarc2966922021-07-21 10:14:03 -0500573 for (const auto& dirEntry : fs::directory_iterator(_archivePath))
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500574 {
Sumit Kumarc2966922021-07-21 10:14:03 -0500575 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 }
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500583 }
Sumit Kumarc2966922021-07-21 10:14:03 -0500584
585 _archiveSize = 0;
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500586 }
587
Matt Spinler7e727a32020-07-07 15:00:17 -0500588 return (_sizes.total > (_maxRepoSize * warningPercentage / 100)) ||
589 (_pelAttributes.size() > _maxNumPELs);
590}
591
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500592std::vector<Repository::AttributesReference>
593 Repository::getAllPELAttributes(SortOrder order) const
594{
595 std::vector<Repository::AttributesReference> attributes;
596
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500597 std::for_each(_pelAttributes.begin(), _pelAttributes.end(),
598 [&attributes](auto& pelEntry) {
599 attributes.push_back(pelEntry);
600 });
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500601
602 std::sort(attributes.begin(), attributes.end(),
603 [order](const auto& left, const auto& right) {
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500604 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 });
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500610
611 return attributes;
612}
613
Sumit Kumar027bf282022-01-24 11:25:19 -0600614std::vector<uint32_t>
615 Repository::prune(const std::vector<uint32_t>& idsWithHwIsoEntry)
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500616{
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
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500679 IsPELTypeFunc isAnyPEL = [](const PELAttributes& /*pel*/) { return true; };
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500680
681 // Check all 4 categories, which will result in at most 90%
682 // usage (15 + 30 + 15 + 30).
Sumit Kumar027bf282022-01-24 11:25:19 -0600683 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);
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500690
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 {
Sumit Kumar027bf282022-01-24 11:25:19 -0600695 removePELs(tooManyPELsLimit, isAnyPEL, idsWithHwIsoEntry, obmcLogIDs);
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500696 }
697
698 if (!obmcLogIDs.empty())
699 {
Matt Spinler45796e82022-07-01 11:25:27 -0500700 std::string m = "Number of PELs removed to save space: " +
701 std::to_string(obmcLogIDs.size());
702 log<level::INFO>(m.c_str());
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500703 }
704
705 return obmcLogIDs;
706}
707
Matt Spinler45796e82022-07-01 11:25:27 -0500708void Repository::removePELs(const IsOverLimitFunc& isOverLimit,
709 const IsPELTypeFunc& isPELType,
Sumit Kumar027bf282022-01-24 11:25:19 -0600710 const std::vector<uint32_t>& idsWithHwIsoEntry,
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500711 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) {
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500728 return pel.hmcState == TransmissionState::acked;
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500729 },
730
731 [](const auto& pel) {
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500732 return pel.hostState == TransmissionState::acked;
733 },
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500734
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500735 [](const auto& pel) {
736 return pel.hostState == TransmissionState::sent;
737 },
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500738
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500739 [](const auto& /*pel*/) { return true; }};
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500740
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;
Sumit Kumar027bf282022-01-24 11:25:19 -0600749
750 auto idFound = std::find(idsWithHwIsoEntry.begin(),
751 idsWithHwIsoEntry.end(), removedID);
752 if (idFound != idsWithHwIsoEntry.end())
753 {
754 ++it;
755 continue;
756 }
757
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500758 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
Sumit Kumar2ccdcef2021-07-31 10:04:58 -0500782void 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
Matt Spinler89fa0822019-07-17 13:54:30 -0500794} // namespace pels
795} // namespace openpower