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/lg2.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 namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error; 33 34 constexpr size_t warningPercentage = 95; 35 36 /** 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 */ 45 size_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; 53 lg2::error("Call to stat() failed on {FILE} with errno {ERRNO}", "FILE", 54 file.native(), "ERRNO", e); 55 abort(); 56 } 57 58 return statData.st_blocks * statBlockSize; 59 } 60 61 Repository::Repository(const std::filesystem::path& basePath, size_t repoSize, 62 size_t maxNumPELs) : 63 _logPath(basePath / "logs"), 64 _maxRepoSize(repoSize), _maxNumPELs(maxNumPELs), 65 _archivePath(basePath / "logs" / "archive") 66 { 67 if (!fs::exists(_logPath)) 68 { 69 fs::create_directories(_logPath); 70 } 71 72 if (!fs::exists(_archivePath)) 73 { 74 fs::create_directories(_archivePath); 75 } 76 77 restore(); 78 } 79 80 void 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 96 PEL pel{data}; 97 if (pel.valid()) 98 { 99 // 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 (const std::exception& e) 109 { 110 lg2::error( 111 "Failed to save PEL after updating host state, PEL ID = {ID}", 112 "ID", lg2::hex, pel.id()); 113 } 114 } 115 116 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())}; 130 131 using pelID = LogID::Pel; 132 using obmcID = LogID::Obmc; 133 _pelAttributes.emplace( 134 LogID(pelID(pel.id()), obmcID(pel.obmcLogID())), 135 attributes); 136 137 updateRepoStats(attributes, true); 138 } 139 else 140 { 141 lg2::error( 142 "Found invalid PEL file {FILE} while restoring. Removing.", 143 "FILE", dirEntry.path()); 144 fs::remove(dirEntry.path()); 145 } 146 } 147 catch (const std::exception& e) 148 { 149 lg2::error("Hit exception while restoring PEL file {FILE}: {ERROR}", 150 "FILE", dirEntry.path(), "ERROR", e); 151 } 152 } 153 154 // Get size of archive folder 155 for (auto& dirEntry : fs::directory_iterator(_archivePath)) 156 { 157 _archiveSize += getFileDiskSize(dirEntry); 158 } 159 } 160 161 std::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 170 void Repository::add(std::unique_ptr<PEL>& pel) 171 { 172 pel->setHostTransmissionState(TransmissionState::newPEL); 173 pel->setHMCTransmissionState(TransmissionState::newPEL); 174 175 auto path = _logPath / getPELFilename(pel->id(), pel->commitTime()); 176 177 write(*(pel.get()), path); 178 179 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())}; 192 193 using pelID = LogID::Pel; 194 using obmcID = LogID::Obmc; 195 _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())), 196 attributes); 197 198 _lastPelID = pel->id(); 199 200 updateRepoStats(attributes, true); 201 202 processAddCallbacks(*pel); 203 } 204 205 void Repository::write(const PEL& pel, const fs::path& path) 206 { 207 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; 214 fs::remove(path); 215 lg2::error( 216 "Unable to open PEL file {FILE} for writing, errno = {ERRNO}", 217 "FILE", path, "ERRNO", e); 218 throw file_error::Open(); 219 } 220 221 auto data = pel.data(); 222 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; 229 file.close(); 230 fs::remove(path); 231 lg2::error("Unable to write PEL file {FILE}, errno = {ERRNO}", "FILE", 232 path, "ERRNO", e); 233 throw file_error::Write(); 234 } 235 } 236 237 std::optional<Repository::LogID> Repository::remove(const LogID& id) 238 { 239 auto pel = findPEL(id); 240 if (pel == _pelAttributes.end()) 241 { 242 return std::nullopt; 243 } 244 245 LogID actualID = pel->first; 246 updateRepoStats(pel->second, false); 247 248 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); 251 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 268 _pelAttributes.erase(pel); 269 270 processDeleteCallbacks(actualID.pelID.id); 271 272 return actualID; 273 } 274 275 std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id) 276 { 277 auto pel = findPEL(id); 278 if (pel != _pelAttributes.end()) 279 { 280 std::ifstream file{pel->second.path.c_str()}; 281 if (!file.good()) 282 { 283 auto e = errno; 284 lg2::error("Unable to open PEL file {FILE}, errno = {ERRNO}", 285 "FILE", pel->second.path, "ERRNO", e); 286 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 297 std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id) 298 { 299 auto pel = findPEL(id); 300 if (pel != _pelAttributes.end()) 301 { 302 int fd = open(pel->second.path.c_str(), O_RDONLY | O_NONBLOCK); 303 if (fd == -1) 304 { 305 auto e = errno; 306 lg2::error("Unable to open PEL file {FILE}, errno = {ERRNO}", 307 "FILE", pel->second.path, "ERRNO", e); 308 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. 313 return fd; 314 } 315 return std::nullopt; 316 } 317 318 void Repository::for_each(ForEachFunc func) const 319 { 320 for (const auto& [id, attributes] : _pelAttributes) 321 { 322 std::ifstream file{attributes.path}; 323 324 if (!file.good()) 325 { 326 auto e = errno; 327 lg2::error( 328 "Repository::for_each: Unable to open PEL file {FILE}, errno = {ERRNO}", 329 "FILE", attributes.path, "ERRNO", e); 330 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 } 346 catch (const std::exception& e) 347 { 348 lg2::error("Repository::for_each function exception: {ERROR}", 349 "ERROR", e); 350 } 351 } 352 } 353 354 void Repository::processAddCallbacks(const PEL& pel) const 355 { 356 for (auto& [name, func] : _addSubscriptions) 357 { 358 try 359 { 360 func(pel); 361 } 362 catch (const std::exception& e) 363 { 364 lg2::error( 365 "PEL Repository add callback exception. Name = {NAME}, Error = {ERROR}", 366 "NAME", name, "ERROR", e); 367 } 368 } 369 } 370 371 void Repository::processDeleteCallbacks(uint32_t id) const 372 { 373 for (auto& [name, func] : _deleteSubscriptions) 374 { 375 try 376 { 377 func(id); 378 } 379 catch (const std::exception& e) 380 { 381 lg2::error( 382 "PEL Repository delete callback exception. Name = {NAME}, Error = {ERROR}", 383 "NAME", name, "ERROR", e); 384 } 385 } 386 } 387 388 std::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 400 void 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); 410 return true; 411 }; 412 413 try 414 { 415 updatePEL(attr->second.path, func); 416 } 417 catch (const std::exception& e) 418 { 419 lg2::error( 420 "Unable to update PEL host transmission state. Path = {PATH}, Error = {ERROR}", 421 "PATH", attr->second.path, "ERROR", e); 422 } 423 } 424 } 425 426 void 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); 436 return true; 437 }; 438 439 try 440 { 441 updatePEL(attr->second.path, func); 442 } 443 catch (const std::exception& e) 444 { 445 lg2::error( 446 "Unable to update PEL HMC transmission state. Path = {PATH}, Error = {ERROR}", 447 "PATH", attr->second.path, "ERROR", e); 448 } 449 } 450 } 451 452 bool 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 { 463 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 } 482 483 write(pel, path); 484 return true; 485 } 486 } 487 else 488 { 489 throw std::runtime_error( 490 "Unable to read a valid PEL when trying to update it"); 491 } 492 return false; 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 lg2::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 lg2::info("Repository::sizeWarning: Could not delete " 579 "file {FILE} in PEL archive", 580 "FILE", dirEntry.path()); 581 } 582 } 583 584 _archiveSize = 0; 585 } 586 587 return (_sizes.total > (_maxRepoSize * warningPercentage / 100)) || 588 (_pelAttributes.size() > _maxNumPELs); 589 } 590 591 std::vector<Repository::AttributesReference> 592 Repository::getAllPELAttributes(SortOrder order) const 593 { 594 std::vector<Repository::AttributesReference> attributes; 595 596 std::for_each( 597 _pelAttributes.begin(), _pelAttributes.end(), 598 [&attributes](auto& pelEntry) { attributes.push_back(pelEntry); }); 599 600 std::sort(attributes.begin(), attributes.end(), 601 [order](const auto& left, const auto& right) { 602 if (order == SortOrder::ascending) 603 { 604 return left.get().second.path < right.get().second.path; 605 } 606 return left.get().second.path > right.get().second.path; 607 }); 608 609 return attributes; 610 } 611 612 std::vector<uint32_t> 613 Repository::prune(const std::vector<uint32_t>& idsWithHwIsoEntry) 614 { 615 std::vector<uint32_t> obmcLogIDs; 616 lg2::info("Pruning PEL repository that takes up {TOTAL} bytes and has " 617 "{NUM_PELS} PELs", 618 "TOTAL", _sizes.total, "NUM_PELS", _pelAttributes.size()); 619 620 // Set up the 5 functions to check if the PEL category 621 // is still over its limits. 622 623 // BMC informational PELs should only take up 15% 624 IsOverLimitFunc overBMCInfoLimit = [this]() { 625 return _sizes.bmcInfo > _maxRepoSize * 15 / 100; 626 }; 627 628 // BMC non informational PELs should only take up 30% 629 IsOverLimitFunc overBMCNonInfoLimit = [this]() { 630 return _sizes.bmcServiceable > _maxRepoSize * 30 / 100; 631 }; 632 633 // Non BMC informational PELs should only take up 15% 634 IsOverLimitFunc overNonBMCInfoLimit = [this]() { 635 return _sizes.nonBMCInfo > _maxRepoSize * 15 / 100; 636 }; 637 638 // Non BMC non informational PELs should only take up 15% 639 IsOverLimitFunc overNonBMCNonInfoLimit = [this]() { 640 return _sizes.nonBMCServiceable > _maxRepoSize * 30 / 100; 641 }; 642 643 // Bring the total number of PELs down to 80% of the max 644 IsOverLimitFunc tooManyPELsLimit = [this]() { 645 return _pelAttributes.size() > _maxNumPELs * 80 / 100; 646 }; 647 648 // Set up the functions to determine which category a PEL is in. 649 // TODO: Return false in these functions if a PEL caused a guard record. 650 651 // A BMC informational PEL 652 IsPELTypeFunc isBMCInfo = [](const PELAttributes& pel) { 653 return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) && 654 !Repository::isServiceableSev(pel); 655 }; 656 657 // A BMC non informational PEL 658 IsPELTypeFunc isBMCNonInfo = [](const PELAttributes& pel) { 659 return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) && 660 Repository::isServiceableSev(pel); 661 }; 662 663 // A non BMC informational PEL 664 IsPELTypeFunc isNonBMCInfo = [](const PELAttributes& pel) { 665 return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) && 666 !Repository::isServiceableSev(pel); 667 }; 668 669 // A non BMC non informational PEL 670 IsPELTypeFunc isNonBMCNonInfo = [](const PELAttributes& pel) { 671 return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) && 672 Repository::isServiceableSev(pel); 673 }; 674 675 // When counting PELs, count every PEL 676 IsPELTypeFunc isAnyPEL = [](const PELAttributes& /*pel*/) { return true; }; 677 678 // Check all 4 categories, which will result in at most 90% 679 // usage (15 + 30 + 15 + 30). 680 removePELs(overBMCInfoLimit, isBMCInfo, idsWithHwIsoEntry, obmcLogIDs); 681 removePELs(overBMCNonInfoLimit, isBMCNonInfo, idsWithHwIsoEntry, 682 obmcLogIDs); 683 removePELs(overNonBMCInfoLimit, isNonBMCInfo, idsWithHwIsoEntry, 684 obmcLogIDs); 685 removePELs(overNonBMCNonInfoLimit, isNonBMCNonInfo, idsWithHwIsoEntry, 686 obmcLogIDs); 687 688 // After the above pruning check if there are still too many PELs, 689 // which can happen depending on PEL sizes. 690 if (_pelAttributes.size() > _maxNumPELs) 691 { 692 removePELs(tooManyPELsLimit, isAnyPEL, idsWithHwIsoEntry, obmcLogIDs); 693 } 694 695 if (!obmcLogIDs.empty()) 696 { 697 lg2::info("Number of PELs removed to save space: {NUM_PELS}", 698 "NUM_PELS", obmcLogIDs.size()); 699 } 700 701 return obmcLogIDs; 702 } 703 704 void Repository::removePELs(const IsOverLimitFunc& isOverLimit, 705 const IsPELTypeFunc& isPELType, 706 const std::vector<uint32_t>& idsWithHwIsoEntry, 707 std::vector<uint32_t>& removedBMCLogIDs) 708 { 709 if (!isOverLimit()) 710 { 711 return; 712 } 713 714 auto attributes = getAllPELAttributes(SortOrder::ascending); 715 716 // Make 4 passes on the PELs, stopping as soon as isOverLimit 717 // returns false. 718 // Pass 1: only delete HMC acked PELs 719 // Pass 2: only delete OS acked PELs 720 // Pass 3: only delete PHYP sent PELs 721 // Pass 4: delete all PELs 722 static const std::vector<std::function<bool(const PELAttributes& pel)>> 723 stateChecks{[](const auto& pel) { 724 return pel.hmcState == TransmissionState::acked; 725 }, 726 727 [](const auto& pel) { 728 return pel.hostState == TransmissionState::acked; 729 }, 730 731 [](const auto& pel) { 732 return pel.hostState == TransmissionState::sent; 733 }, 734 735 [](const auto& /*pel*/) { return true; }}; 736 737 for (const auto& stateCheck : stateChecks) 738 { 739 for (auto it = attributes.begin(); it != attributes.end();) 740 { 741 const auto& pel = it->get(); 742 if (isPELType(pel.second) && stateCheck(pel.second)) 743 { 744 auto removedID = pel.first.obmcID.id; 745 746 auto idFound = std::find(idsWithHwIsoEntry.begin(), 747 idsWithHwIsoEntry.end(), removedID); 748 if (idFound != idsWithHwIsoEntry.end()) 749 { 750 ++it; 751 continue; 752 } 753 754 remove(pel.first); 755 756 removedBMCLogIDs.push_back(removedID); 757 758 attributes.erase(it); 759 760 if (!isOverLimit()) 761 { 762 break; 763 } 764 } 765 else 766 { 767 ++it; 768 } 769 } 770 771 if (!isOverLimit()) 772 { 773 break; 774 } 775 } 776 } 777 778 void Repository::archivePEL(const PEL& pel) 779 { 780 if (pel.valid()) 781 { 782 auto path = _archivePath / getPELFilename(pel.id(), pel.commitTime()); 783 784 write(pel, path); 785 786 _archiveSize += getFileDiskSize(path); 787 } 788 } 789 790 } // namespace pels 791 } // namespace openpower 792