blob: bc5249d4b7470f1ca3551f057ada04e3e6feacb7 [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 Spinlerdb3f2792023-07-07 16:25:26 -050021#include <phosphor-logging/lg2.hpp>
Matt Spinler89fa0822019-07-17 13:54:30 -050022#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;
Matt Spinler89fa0822019-07-17 13:54:30 -050032namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
33
Matt Spinler7e727a32020-07-07 15:00:17 -050034constexpr size_t warningPercentage = 95;
35
Matt Spinlerdd325c32020-07-07 11:01:54 -050036/**
37 * @brief Returns the amount of space the file uses on disk.
38 *
39 * This is different than just the regular size of the file.
40 *
41 * @param[in] file - The file to get the size of
42 *
43 * @return size_t The disk space the file uses
44 */
45size_t getFileDiskSize(const std::filesystem::path& file)
46{
47 constexpr size_t statBlockSize = 512;
48 struct stat statData;
49 auto rc = stat(file.c_str(), &statData);
50 if (rc != 0)
51 {
52 auto e = errno;
Matt Spinlerdb3f2792023-07-07 16:25:26 -050053 lg2::error("Call to stat() failed on {FILE} with errno {ERRNO}", "FILE",
54 file.native(), "ERRNO", e);
Matt Spinlerdd325c32020-07-07 11:01:54 -050055 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 }
Patrick Williams66491c62021-10-06 12:23:37 -0500108 catch (const std::exception& e)
Matt Spinlera3c12a42019-11-21 13:25:32 -0600109 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500110 lg2::error(
111 "Failed to save PEL after updating host state, PEL ID = {ID}",
112 "ID", lg2::hex, pel.id());
Matt Spinlera3c12a42019-11-21 13:25:32 -0600113 }
114 }
115
Matt Spinler8e65f4e2023-05-02 13:40:08 -0500116 PELAttributes attributes{
117 dirEntry.path(),
118 getFileDiskSize(dirEntry.path()),
119 pel.privateHeader().creatorID(),
120 pel.userHeader().subsystem(),
121 pel.userHeader().severity(),
122 pel.userHeader().actionFlags(),
123 pel.hostTransmissionState(),
124 pel.hmcTransmissionState(),
125 pel.plid(),
126 pel.getDeconfigFlag(),
127 pel.getGuardFlag(),
128 getMillisecondsSinceEpoch(
129 pel.privateHeader().createTimestamp())};
Matt Spinler0ff00482019-11-06 16:19:46 -0600130
Matt Spinler475e5742019-07-18 16:09:49 -0500131 using pelID = LogID::Pel;
132 using obmcID = LogID::Obmc;
Matt Spinler0ff00482019-11-06 16:19:46 -0600133 _pelAttributes.emplace(
Matt Spinler475e5742019-07-18 16:09:49 -0500134 LogID(pelID(pel.id()), obmcID(pel.obmcLogID())),
Matt Spinler0ff00482019-11-06 16:19:46 -0600135 attributes);
Matt Spinlerb188f782020-07-07 11:18:12 -0500136
137 updateRepoStats(attributes, true);
Matt Spinler475e5742019-07-18 16:09:49 -0500138 }
139 else
140 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500141 lg2::error(
142 "Found invalid PEL file {FILE} while restoring. Removing.",
143 "FILE", dirEntry.path());
Matt Spinler475e5742019-07-18 16:09:49 -0500144 fs::remove(dirEntry.path());
145 }
146 }
Patrick Williams66491c62021-10-06 12:23:37 -0500147 catch (const std::exception& e)
Matt Spinler475e5742019-07-18 16:09:49 -0500148 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500149 lg2::error("Hit exception while restoring PEL file {FILE}: {ERROR}",
150 "FILE", dirEntry.path(), "ERROR", e);
Matt Spinler475e5742019-07-18 16:09:49 -0500151 }
152 }
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500153
154 // Get size of archive folder
155 for (auto& dirEntry : fs::directory_iterator(_archivePath))
156 {
157 _archiveSize += getFileDiskSize(dirEntry);
158 }
Matt Spinler89fa0822019-07-17 13:54:30 -0500159}
160
161std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time)
162{
163 char name[50];
164 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB,
165 time.yearLSB, time.month, time.day, time.hour, time.minutes,
166 time.seconds, time.hundredths, pelID);
167 return std::string{name};
168}
169
170void Repository::add(std::unique_ptr<PEL>& pel)
171{
Matt Spinlerdf43a302019-11-21 13:16:56 -0600172 pel->setHostTransmissionState(TransmissionState::newPEL);
173 pel->setHMCTransmissionState(TransmissionState::newPEL);
174
Matt Spinler89fa0822019-07-17 13:54:30 -0500175 auto path = _logPath / getPELFilename(pel->id(), pel->commitTime());
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600176
177 write(*(pel.get()), path);
178
Matt Spinler8e65f4e2023-05-02 13:40:08 -0500179 PELAttributes attributes{
180 path,
181 getFileDiskSize(path),
182 pel->privateHeader().creatorID(),
183 pel->userHeader().subsystem(),
184 pel->userHeader().severity(),
185 pel->userHeader().actionFlags(),
186 pel->hostTransmissionState(),
187 pel->hmcTransmissionState(),
188 pel->plid(),
189 pel->getDeconfigFlag(),
190 pel->getGuardFlag(),
191 getMillisecondsSinceEpoch(pel->privateHeader().createTimestamp())};
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600192
193 using pelID = LogID::Pel;
194 using obmcID = LogID::Obmc;
195 _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())),
196 attributes);
197
Matt Spinler44893cc2020-08-26 11:34:17 -0500198 _lastPelID = pel->id();
199
Matt Spinlerb188f782020-07-07 11:18:12 -0500200 updateRepoStats(attributes, true);
201
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600202 processAddCallbacks(*pel);
203}
204
205void Repository::write(const PEL& pel, const fs::path& path)
206{
Matt Spinler89fa0822019-07-17 13:54:30 -0500207 std::ofstream file{path, std::ios::binary};
208
209 if (!file.good())
210 {
211 // If this fails, the filesystem is probably full so it isn't like
212 // we could successfully create yet another error log here.
213 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500214 fs::remove(path);
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500215 lg2::error(
216 "Unable to open PEL file {FILE} for writing, errno = {ERRNO}",
217 "FILE", path, "ERRNO", e);
Matt Spinler89fa0822019-07-17 13:54:30 -0500218 throw file_error::Open();
219 }
220
Matt Spinlerab1b97f2019-11-07 13:38:07 -0600221 auto data = pel.data();
Matt Spinler89fa0822019-07-17 13:54:30 -0500222 file.write(reinterpret_cast<const char*>(data.data()), data.size());
223
224 if (file.fail())
225 {
226 // Same note as above about not being able to create an error log
227 // for this case even if we wanted.
228 auto e = errno;
Matt Spinler89fa0822019-07-17 13:54:30 -0500229 file.close();
230 fs::remove(path);
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500231 lg2::error("Unable to write PEL file {FILE}, errno = {ERRNO}", "FILE",
232 path, "ERRNO", e);
Matt Spinler89fa0822019-07-17 13:54:30 -0500233 throw file_error::Write();
234 }
Matt Spinler475e5742019-07-18 16:09:49 -0500235}
236
Matt Spinler52602e32020-07-15 12:37:28 -0500237std::optional<Repository::LogID> Repository::remove(const LogID& id)
Matt Spinler475e5742019-07-18 16:09:49 -0500238{
239 auto pel = findPEL(id);
Patrick Williamsff6b5982021-04-22 09:04:17 -0500240 if (pel == _pelAttributes.end())
Matt Spinler475e5742019-07-18 16:09:49 -0500241 {
Patrick Williamsff6b5982021-04-22 09:04:17 -0500242 return std::nullopt;
Matt Spinler5f5352e2020-03-05 16:23:27 -0600243 }
Matt Spinler52602e32020-07-15 12:37:28 -0500244
Patrick Williamsff6b5982021-04-22 09:04:17 -0500245 LogID actualID = pel->first;
246 updateRepoStats(pel->second, false);
247
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500248 lg2::debug(
249 "Removing PEL from repository, PEL ID = {PEL_ID}, BMC log ID = {BMC_ID}",
250 "PEL_ID", lg2::hex, actualID.pelID.id, "BMC_ID", actualID.obmcID.id);
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500251
252 if (fs::exists(pel->second.path))
253 {
254 // Check for existense of new archive folder
255 if (!fs::exists(_archivePath))
256 {
257 fs::create_directories(_archivePath);
258 }
259
260 // Move log file to archive folder
261 auto fileName = _archivePath / pel->second.path.filename();
262 fs::rename(pel->second.path, fileName);
263
264 // Update size of file
265 _archiveSize += getFileDiskSize(fileName);
266 }
267
Patrick Williamsff6b5982021-04-22 09:04:17 -0500268 _pelAttributes.erase(pel);
269
270 processDeleteCallbacks(actualID.pelID.id);
271
Matt Spinler52602e32020-07-15 12:37:28 -0500272 return actualID;
Matt Spinler89fa0822019-07-17 13:54:30 -0500273}
274
Matt Spinler2813f362019-07-19 12:45:28 -0500275std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id)
276{
277 auto pel = findPEL(id);
Matt Spinler0ff00482019-11-06 16:19:46 -0600278 if (pel != _pelAttributes.end())
Matt Spinler2813f362019-07-19 12:45:28 -0500279 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600280 std::ifstream file{pel->second.path.c_str()};
Matt Spinler2813f362019-07-19 12:45:28 -0500281 if (!file.good())
282 {
283 auto e = errno;
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500284 lg2::error("Unable to open PEL file {FILE}, errno = {ERRNO}",
285 "FILE", pel->second.path, "ERRNO", e);
Matt Spinler2813f362019-07-19 12:45:28 -0500286 throw file_error::Open();
287 }
288
289 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
290 std::istreambuf_iterator<char>()};
291 return data;
292 }
293
294 return std::nullopt;
295}
296
Matt Spinler6d512242019-12-09 13:44:17 -0600297std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id)
298{
299 auto pel = findPEL(id);
300 if (pel != _pelAttributes.end())
301 {
Matt Spinler32a6df62023-01-12 16:30:40 -0600302 int fd = open(pel->second.path.c_str(), O_RDONLY | O_NONBLOCK);
303 if (fd == -1)
Matt Spinler6d512242019-12-09 13:44:17 -0600304 {
305 auto e = errno;
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500306 lg2::error("Unable to open PEL file {FILE}, errno = {ERRNO}",
307 "FILE", pel->second.path, "ERRNO", e);
Matt Spinler6d512242019-12-09 13:44:17 -0600308 throw file_error::Open();
309 }
310
311 // Must leave the file open here. It will be closed by sdbusplus
312 // when it sends it back over D-Bus.
Matt Spinler32a6df62023-01-12 16:30:40 -0600313 return fd;
Matt Spinler6d512242019-12-09 13:44:17 -0600314 }
315 return std::nullopt;
316}
317
Matt Spinler1ea78802019-11-01 13:04:59 -0500318void Repository::for_each(ForEachFunc func) const
319{
Matt Spinler0ff00482019-11-06 16:19:46 -0600320 for (const auto& [id, attributes] : _pelAttributes)
Matt Spinler1ea78802019-11-01 13:04:59 -0500321 {
Matt Spinler0ff00482019-11-06 16:19:46 -0600322 std::ifstream file{attributes.path};
Matt Spinler1ea78802019-11-01 13:04:59 -0500323
324 if (!file.good())
325 {
326 auto e = errno;
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500327 lg2::error(
328 "Repository::for_each: Unable to open PEL file {FILE}, errno = {ERRNO}",
329 "FILE", attributes.path, "ERRNO", e);
Matt Spinler1ea78802019-11-01 13:04:59 -0500330 continue;
331 }
332
333 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
334 std::istreambuf_iterator<char>()};
335 file.close();
336
337 PEL pel{data};
338
339 try
340 {
341 if (func(pel))
342 {
343 break;
344 }
345 }
Patrick Williams66491c62021-10-06 12:23:37 -0500346 catch (const std::exception& e)
Matt Spinler1ea78802019-11-01 13:04:59 -0500347 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500348 lg2::error("Repository::for_each function exception: {ERROR}",
349 "ERROR", e);
Matt Spinler1ea78802019-11-01 13:04:59 -0500350 }
351 }
352}
353
Matt Spinler421f6532019-11-06 15:40:45 -0600354void Repository::processAddCallbacks(const PEL& pel) const
355{
356 for (auto& [name, func] : _addSubscriptions)
357 {
358 try
359 {
360 func(pel);
361 }
Patrick Williams66491c62021-10-06 12:23:37 -0500362 catch (const std::exception& e)
Matt Spinler421f6532019-11-06 15:40:45 -0600363 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500364 lg2::error(
365 "PEL Repository add callback exception. Name = {NAME}, Error = {ERROR}",
366 "NAME", name, "ERROR", e);
Matt Spinler421f6532019-11-06 15:40:45 -0600367 }
368 }
369}
370
371void Repository::processDeleteCallbacks(uint32_t id) const
372{
373 for (auto& [name, func] : _deleteSubscriptions)
374 {
375 try
376 {
377 func(id);
378 }
Patrick Williams66491c62021-10-06 12:23:37 -0500379 catch (const std::exception& e)
Matt Spinler421f6532019-11-06 15:40:45 -0600380 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500381 lg2::error(
382 "PEL Repository delete callback exception. Name = {NAME}, Error = {ERROR}",
383 "NAME", name, "ERROR", e);
Matt Spinler421f6532019-11-06 15:40:45 -0600384 }
385 }
386}
387
Matt Spinler0ff00482019-11-06 16:19:46 -0600388std::optional<std::reference_wrapper<const Repository::PELAttributes>>
389 Repository::getPELAttributes(const LogID& id) const
390{
391 auto pel = findPEL(id);
392 if (pel != _pelAttributes.end())
393 {
394 return pel->second;
395 }
396
397 return std::nullopt;
398}
399
Matt Spinler29d18c12019-11-21 13:31:27 -0600400void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state)
401{
402 LogID id{LogID::Pel{pelID}};
403 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
404 [&id](const auto& a) { return a.first == id; });
405
406 if ((attr != _pelAttributes.end()) && (attr->second.hostState != state))
407 {
408 PELUpdateFunc func = [state](PEL& pel) {
409 pel.setHostTransmissionState(state);
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500410 return true;
Matt Spinler29d18c12019-11-21 13:31:27 -0600411 };
412
413 try
414 {
415 updatePEL(attr->second.path, func);
Matt Spinler29d18c12019-11-21 13:31:27 -0600416 }
Patrick Williams66491c62021-10-06 12:23:37 -0500417 catch (const std::exception& e)
Matt Spinler29d18c12019-11-21 13:31:27 -0600418 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500419 lg2::error(
420 "Unable to update PEL host transmission state. Path = {PATH}, Error = {ERROR}",
421 "PATH", attr->second.path, "ERROR", e);
Matt Spinler29d18c12019-11-21 13:31:27 -0600422 }
423 }
424}
425
426void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state)
427{
428 LogID id{LogID::Pel{pelID}};
429 auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
430 [&id](const auto& a) { return a.first == id; });
431
432 if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state))
433 {
434 PELUpdateFunc func = [state](PEL& pel) {
435 pel.setHMCTransmissionState(state);
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500436 return true;
Matt Spinler29d18c12019-11-21 13:31:27 -0600437 };
438
439 try
440 {
441 updatePEL(attr->second.path, func);
Matt Spinler29d18c12019-11-21 13:31:27 -0600442 }
Patrick Williams66491c62021-10-06 12:23:37 -0500443 catch (const std::exception& e)
Matt Spinler29d18c12019-11-21 13:31:27 -0600444 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500445 lg2::error(
446 "Unable to update PEL HMC transmission state. Path = {PATH}, Error = {ERROR}",
447 "PATH", attr->second.path, "ERROR", e);
Matt Spinler29d18c12019-11-21 13:31:27 -0600448 }
449 }
450}
451
452void Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc)
453{
454 std::ifstream file{path};
455 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
456 std::istreambuf_iterator<char>()};
457 file.close();
458
459 PEL pel{data};
460
461 if (pel.valid())
462 {
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500463 if (updateFunc(pel))
464 {
465 // Three attribute fields can change post creation from
466 // an updatePEL call:
467 // - hmcTransmissionState - When HMC acks a PEL
468 // - hostTransmissionState - When host acks a PEL
469 // - deconfig flag - Can be cleared for PELs that call out
470 // hotplugged FRUs.
471 // Make sure they're up to date.
472 LogID id{LogID::Pel(pel.id())};
473 auto attr =
474 std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
475 [&id](const auto& a) { return a.first == id; });
476 if (attr != _pelAttributes.end())
477 {
478 attr->second.hmcState = pel.hmcTransmissionState();
479 attr->second.hostState = pel.hostTransmissionState();
480 attr->second.deconfig = pel.getDeconfigFlag();
481 }
Matt Spinler29d18c12019-11-21 13:31:27 -0600482
Matt Spinlerd0ccda32023-05-04 09:59:59 -0500483 write(pel, path);
484 }
Matt Spinler29d18c12019-11-21 13:31:27 -0600485 }
486 else
487 {
488 throw std::runtime_error(
489 "Unable to read a valid PEL when trying to update it");
490 }
491}
492
Matt Spinlerb188f782020-07-07 11:18:12 -0500493bool Repository::isServiceableSev(const PELAttributes& pel)
494{
495 auto sevType = static_cast<SeverityType>(pel.severity & 0xF0);
Patrick Williams2544b412022-10-04 08:41:06 -0500496 auto sevPVEntry = pel_values::findByValue(pel.severity,
497 pel_values::severityValues);
Matt Spinlerb188f782020-07-07 11:18:12 -0500498 std::string sevName = std::get<pel_values::registryNamePos>(*sevPVEntry);
499
500 bool check1 = (sevType == SeverityType::predictive) ||
501 (sevType == SeverityType::unrecoverable) ||
502 (sevType == SeverityType::critical);
503
504 bool check2 = ((sevType == SeverityType::recovered) ||
505 (sevName == "symptom_recovered")) &&
506 !pel.actionFlags.test(hiddenFlagBit);
507
508 bool check3 = (sevName == "symptom_predictive") ||
509 (sevName == "symptom_unrecoverable") ||
510 (sevName == "symptom_critical");
511
512 return check1 || check2 || check3;
513}
514
515void Repository::updateRepoStats(const PELAttributes& pel, bool pelAdded)
516{
517 auto isServiceable = Repository::isServiceableSev(pel);
518 auto bmcPEL = CreatorID::openBMC == static_cast<CreatorID>(pel.creator);
519
520 auto adjustSize = [pelAdded, &pel](auto& runningSize) {
521 if (pelAdded)
522 {
523 runningSize += pel.sizeOnDisk;
524 }
525 else
526 {
527 runningSize = std::max(static_cast<int64_t>(runningSize) -
528 static_cast<int64_t>(pel.sizeOnDisk),
529 static_cast<int64_t>(0));
530 }
531 };
532
533 adjustSize(_sizes.total);
534
535 if (bmcPEL)
536 {
537 adjustSize(_sizes.bmc);
538 if (isServiceable)
539 {
540 adjustSize(_sizes.bmcServiceable);
541 }
542 else
543 {
544 adjustSize(_sizes.bmcInfo);
545 }
546 }
547 else
548 {
549 adjustSize(_sizes.nonBMC);
550 if (isServiceable)
551 {
552 adjustSize(_sizes.nonBMCServiceable);
553 }
554 else
555 {
556 adjustSize(_sizes.nonBMCInfo);
557 }
558 }
559}
560
Sumit Kumarc2966922021-07-21 10:14:03 -0500561bool Repository::sizeWarning()
Matt Spinler7e727a32020-07-07 15:00:17 -0500562{
Sumit Kumarc2966922021-07-21 10:14:03 -0500563 std::error_code ec;
564
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500565 if ((_archiveSize > 0) && ((_sizes.total + _archiveSize) >
566 ((_maxRepoSize * warningPercentage) / 100)))
567 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500568 lg2::info(
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500569 "Repository::sizeWarning function:Deleting the files in archive");
570
Sumit Kumarc2966922021-07-21 10:14:03 -0500571 for (const auto& dirEntry : fs::directory_iterator(_archivePath))
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500572 {
Sumit Kumarc2966922021-07-21 10:14:03 -0500573 fs::remove(dirEntry.path(), ec);
574 if (ec)
575 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500576 lg2::info("Repository::sizeWarning: Could not delete "
577 "file {FILE} in PEL archive",
578 "FILE", dirEntry.path());
Sumit Kumarc2966922021-07-21 10:14:03 -0500579 }
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500580 }
Sumit Kumarc2966922021-07-21 10:14:03 -0500581
582 _archiveSize = 0;
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500583 }
584
Matt Spinler7e727a32020-07-07 15:00:17 -0500585 return (_sizes.total > (_maxRepoSize * warningPercentage / 100)) ||
586 (_pelAttributes.size() > _maxNumPELs);
587}
588
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500589std::vector<Repository::AttributesReference>
590 Repository::getAllPELAttributes(SortOrder order) const
591{
592 std::vector<Repository::AttributesReference> attributes;
593
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500594 std::for_each(_pelAttributes.begin(), _pelAttributes.end(),
595 [&attributes](auto& pelEntry) {
596 attributes.push_back(pelEntry);
597 });
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500598
599 std::sort(attributes.begin(), attributes.end(),
600 [order](const auto& left, const auto& right) {
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500601 if (order == SortOrder::ascending)
602 {
603 return left.get().second.path < right.get().second.path;
604 }
605 return left.get().second.path > right.get().second.path;
606 });
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500607
608 return attributes;
609}
610
Sumit Kumar027bf282022-01-24 11:25:19 -0600611std::vector<uint32_t>
612 Repository::prune(const std::vector<uint32_t>& idsWithHwIsoEntry)
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500613{
614 std::vector<uint32_t> obmcLogIDs;
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500615 lg2::info("Pruning PEL repository that takes up {TOTAL} bytes and has "
616 "{NUM_PELS} PELs",
617 "TOTAL", _sizes.total, "NUM_PELS", _pelAttributes.size());
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500618
619 // Set up the 5 functions to check if the PEL category
620 // is still over its limits.
621
622 // BMC informational PELs should only take up 15%
623 IsOverLimitFunc overBMCInfoLimit = [this]() {
624 return _sizes.bmcInfo > _maxRepoSize * 15 / 100;
625 };
626
627 // BMC non informational PELs should only take up 30%
628 IsOverLimitFunc overBMCNonInfoLimit = [this]() {
629 return _sizes.bmcServiceable > _maxRepoSize * 30 / 100;
630 };
631
632 // Non BMC informational PELs should only take up 15%
633 IsOverLimitFunc overNonBMCInfoLimit = [this]() {
634 return _sizes.nonBMCInfo > _maxRepoSize * 15 / 100;
635 };
636
637 // Non BMC non informational PELs should only take up 15%
638 IsOverLimitFunc overNonBMCNonInfoLimit = [this]() {
639 return _sizes.nonBMCServiceable > _maxRepoSize * 30 / 100;
640 };
641
642 // Bring the total number of PELs down to 80% of the max
643 IsOverLimitFunc tooManyPELsLimit = [this]() {
644 return _pelAttributes.size() > _maxNumPELs * 80 / 100;
645 };
646
647 // Set up the functions to determine which category a PEL is in.
648 // TODO: Return false in these functions if a PEL caused a guard record.
649
650 // A BMC informational PEL
651 IsPELTypeFunc isBMCInfo = [](const PELAttributes& pel) {
652 return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) &&
653 !Repository::isServiceableSev(pel);
654 };
655
656 // A BMC non informational PEL
657 IsPELTypeFunc isBMCNonInfo = [](const PELAttributes& pel) {
658 return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) &&
659 Repository::isServiceableSev(pel);
660 };
661
662 // A non BMC informational PEL
663 IsPELTypeFunc isNonBMCInfo = [](const PELAttributes& pel) {
664 return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) &&
665 !Repository::isServiceableSev(pel);
666 };
667
668 // A non BMC non informational PEL
669 IsPELTypeFunc isNonBMCNonInfo = [](const PELAttributes& pel) {
670 return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) &&
671 Repository::isServiceableSev(pel);
672 };
673
674 // When counting PELs, count every PEL
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500675 IsPELTypeFunc isAnyPEL = [](const PELAttributes& /*pel*/) { return true; };
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500676
677 // Check all 4 categories, which will result in at most 90%
678 // usage (15 + 30 + 15 + 30).
Sumit Kumar027bf282022-01-24 11:25:19 -0600679 removePELs(overBMCInfoLimit, isBMCInfo, idsWithHwIsoEntry, obmcLogIDs);
680 removePELs(overBMCNonInfoLimit, isBMCNonInfo, idsWithHwIsoEntry,
681 obmcLogIDs);
682 removePELs(overNonBMCInfoLimit, isNonBMCInfo, idsWithHwIsoEntry,
683 obmcLogIDs);
684 removePELs(overNonBMCNonInfoLimit, isNonBMCNonInfo, idsWithHwIsoEntry,
685 obmcLogIDs);
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500686
687 // After the above pruning check if there are still too many PELs,
688 // which can happen depending on PEL sizes.
689 if (_pelAttributes.size() > _maxNumPELs)
690 {
Sumit Kumar027bf282022-01-24 11:25:19 -0600691 removePELs(tooManyPELsLimit, isAnyPEL, idsWithHwIsoEntry, obmcLogIDs);
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500692 }
693
694 if (!obmcLogIDs.empty())
695 {
Matt Spinlerdb3f2792023-07-07 16:25:26 -0500696 lg2::info("Number of PELs removed to save space: {NUM_PELS}",
697 "NUM_PELS", obmcLogIDs.size());
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500698 }
699
700 return obmcLogIDs;
701}
702
Matt Spinler45796e82022-07-01 11:25:27 -0500703void Repository::removePELs(const IsOverLimitFunc& isOverLimit,
704 const IsPELTypeFunc& isPELType,
Sumit Kumar027bf282022-01-24 11:25:19 -0600705 const std::vector<uint32_t>& idsWithHwIsoEntry,
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500706 std::vector<uint32_t>& removedBMCLogIDs)
707{
708 if (!isOverLimit())
709 {
710 return;
711 }
712
713 auto attributes = getAllPELAttributes(SortOrder::ascending);
714
715 // Make 4 passes on the PELs, stopping as soon as isOverLimit
716 // returns false.
717 // Pass 1: only delete HMC acked PELs
718 // Pass 2: only delete OS acked PELs
719 // Pass 3: only delete PHYP sent PELs
720 // Pass 4: delete all PELs
721 static const std::vector<std::function<bool(const PELAttributes& pel)>>
722 stateChecks{[](const auto& pel) {
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500723 return pel.hmcState == TransmissionState::acked;
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500724 },
725
726 [](const auto& pel) {
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500727 return pel.hostState == TransmissionState::acked;
728 },
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500729
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500730 [](const auto& pel) {
731 return pel.hostState == TransmissionState::sent;
732 },
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500733
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500734 [](const auto& /*pel*/) { return true; }};
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500735
736 for (const auto& stateCheck : stateChecks)
737 {
738 for (auto it = attributes.begin(); it != attributes.end();)
739 {
740 const auto& pel = it->get();
741 if (isPELType(pel.second) && stateCheck(pel.second))
742 {
743 auto removedID = pel.first.obmcID.id;
Sumit Kumar027bf282022-01-24 11:25:19 -0600744
745 auto idFound = std::find(idsWithHwIsoEntry.begin(),
746 idsWithHwIsoEntry.end(), removedID);
747 if (idFound != idsWithHwIsoEntry.end())
748 {
749 ++it;
750 continue;
751 }
752
Matt Spinlerb0a8df52020-07-07 14:41:06 -0500753 remove(pel.first);
754
755 removedBMCLogIDs.push_back(removedID);
756
757 attributes.erase(it);
758
759 if (!isOverLimit())
760 {
761 break;
762 }
763 }
764 else
765 {
766 ++it;
767 }
768 }
769
770 if (!isOverLimit())
771 {
772 break;
773 }
774 }
775}
776
Sumit Kumar2ccdcef2021-07-31 10:04:58 -0500777void Repository::archivePEL(const PEL& pel)
778{
779 if (pel.valid())
780 {
781 auto path = _archivePath / getPELFilename(pel.id(), pel.commitTime());
782
783 write(pel, path);
784
785 _archiveSize += getFileDiskSize(path);
786 }
787}
788
Matt Spinler89fa0822019-07-17 13:54:30 -0500789} // namespace pels
790} // namespace openpower