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