1711d51d8SMatt Spinler /** 2711d51d8SMatt Spinler * Copyright © 2019 IBM Corporation 3711d51d8SMatt Spinler * 4711d51d8SMatt Spinler * Licensed under the Apache License, Version 2.0 (the "License"); 5711d51d8SMatt Spinler * you may not use this file except in compliance with the License. 6711d51d8SMatt Spinler * You may obtain a copy of the License at 7711d51d8SMatt Spinler * 8711d51d8SMatt Spinler * http://www.apache.org/licenses/LICENSE-2.0 9711d51d8SMatt Spinler * 10711d51d8SMatt Spinler * Unless required by applicable law or agreed to in writing, software 11711d51d8SMatt Spinler * distributed under the License is distributed on an "AS IS" BASIS, 12711d51d8SMatt Spinler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13711d51d8SMatt Spinler * See the License for the specific language governing permissions and 14711d51d8SMatt Spinler * limitations under the License. 15711d51d8SMatt Spinler */ 1689fa082aSMatt Spinler #include "repository.hpp" 1789fa082aSMatt Spinler 1832a6df6cSMatt Spinler #include <fcntl.h> 19dd325c32SMatt Spinler #include <sys/stat.h> 20dd325c32SMatt Spinler 2189fa082aSMatt Spinler #include <phosphor-logging/log.hpp> 2289fa082aSMatt Spinler #include <xyz/openbmc_project/Common/File/error.hpp> 2389fa082aSMatt Spinler 242544b419SPatrick Williams #include <fstream> 252544b419SPatrick Williams 2689fa082aSMatt Spinler namespace openpower 2789fa082aSMatt Spinler { 2889fa082aSMatt Spinler namespace pels 2989fa082aSMatt Spinler { 3089fa082aSMatt Spinler 3189fa082aSMatt Spinler namespace fs = std::filesystem; 3289fa082aSMatt Spinler using namespace phosphor::logging; 3389fa082aSMatt Spinler namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error; 3489fa082aSMatt Spinler 357e727a39SMatt Spinler constexpr size_t warningPercentage = 95; 367e727a39SMatt Spinler 37dd325c32SMatt Spinler /** 38dd325c32SMatt Spinler * @brief Returns the amount of space the file uses on disk. 39dd325c32SMatt Spinler * 40dd325c32SMatt Spinler * This is different than just the regular size of the file. 41dd325c32SMatt Spinler * 42dd325c32SMatt Spinler * @param[in] file - The file to get the size of 43dd325c32SMatt Spinler * 44dd325c32SMatt Spinler * @return size_t The disk space the file uses 45dd325c32SMatt Spinler */ 46dd325c32SMatt Spinler size_t getFileDiskSize(const std::filesystem::path& file) 47dd325c32SMatt Spinler { 48dd325c32SMatt Spinler constexpr size_t statBlockSize = 512; 49dd325c32SMatt Spinler struct stat statData; 50dd325c32SMatt Spinler auto rc = stat(file.c_str(), &statData); 51dd325c32SMatt Spinler if (rc != 0) 52dd325c32SMatt Spinler { 53dd325c32SMatt Spinler auto e = errno; 54dd325c32SMatt Spinler std::string msg = "call to stat() failed on " + file.native() + 55dd325c32SMatt Spinler " with errno " + std::to_string(e); 56dd325c32SMatt Spinler log<level::ERR>(msg.c_str()); 57dd325c32SMatt Spinler abort(); 58dd325c32SMatt Spinler } 59dd325c32SMatt Spinler 60dd325c32SMatt Spinler return statData.st_blocks * statBlockSize; 61dd325c32SMatt Spinler } 62dd325c32SMatt Spinler 638d5f3a2bSMatt Spinler Repository::Repository(const std::filesystem::path& basePath, size_t repoSize, 648d5f3a2bSMatt Spinler size_t maxNumPELs) : 658d5f3a2bSMatt Spinler _logPath(basePath / "logs"), 661d8835bbSSumit Kumar _maxRepoSize(repoSize), _maxNumPELs(maxNumPELs), 671d8835bbSSumit Kumar _archivePath(basePath / "logs" / "archive") 6889fa082aSMatt Spinler { 6989fa082aSMatt Spinler if (!fs::exists(_logPath)) 7089fa082aSMatt Spinler { 7189fa082aSMatt Spinler fs::create_directories(_logPath); 7289fa082aSMatt Spinler } 73475e574dSMatt Spinler 741d8835bbSSumit Kumar if (!fs::exists(_archivePath)) 751d8835bbSSumit Kumar { 761d8835bbSSumit Kumar fs::create_directories(_archivePath); 771d8835bbSSumit Kumar } 781d8835bbSSumit Kumar 79475e574dSMatt Spinler restore(); 80475e574dSMatt Spinler } 81475e574dSMatt Spinler 82475e574dSMatt Spinler void Repository::restore() 83475e574dSMatt Spinler { 84475e574dSMatt Spinler for (auto& dirEntry : fs::directory_iterator(_logPath)) 85475e574dSMatt Spinler { 86475e574dSMatt Spinler try 87475e574dSMatt Spinler { 88475e574dSMatt Spinler if (!fs::is_regular_file(dirEntry.path())) 89475e574dSMatt Spinler { 90475e574dSMatt Spinler continue; 91475e574dSMatt Spinler } 92475e574dSMatt Spinler 93475e574dSMatt Spinler std::ifstream file{dirEntry.path()}; 94475e574dSMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 95475e574dSMatt Spinler std::istreambuf_iterator<char>()}; 96475e574dSMatt Spinler file.close(); 97475e574dSMatt Spinler 9807eefc54SMatt Spinler PEL pel{data}; 99475e574dSMatt Spinler if (pel.valid()) 100475e574dSMatt Spinler { 101a3c12a48SMatt Spinler // If the host hasn't acked it, reset the host state so 102a3c12a48SMatt Spinler // it will get sent up again. 103a3c12a48SMatt Spinler if (pel.hostTransmissionState() == TransmissionState::sent) 104a3c12a48SMatt Spinler { 105a3c12a48SMatt Spinler pel.setHostTransmissionState(TransmissionState::newPEL); 106a3c12a48SMatt Spinler try 107a3c12a48SMatt Spinler { 108a3c12a48SMatt Spinler write(pel, dirEntry.path()); 109a3c12a48SMatt Spinler } 11066491c61SPatrick Williams catch (const std::exception& e) 111a3c12a48SMatt Spinler { 112a3c12a48SMatt Spinler log<level::ERR>( 113a3c12a48SMatt Spinler "Failed to save PEL after updating host state", 114a3c12a48SMatt Spinler entry("PELID=0x%X", pel.id())); 115a3c12a48SMatt Spinler } 116a3c12a48SMatt Spinler } 117a3c12a48SMatt Spinler 1188e65f4eaSMatt Spinler PELAttributes attributes{ 1198e65f4eaSMatt Spinler dirEntry.path(), 120dd325c32SMatt Spinler getFileDiskSize(dirEntry.path()), 121dd325c32SMatt Spinler pel.privateHeader().creatorID(), 122afb1b46fSVijay Lobo pel.userHeader().subsystem(), 123dd325c32SMatt Spinler pel.userHeader().severity(), 124dd325c32SMatt Spinler pel.userHeader().actionFlags(), 125dd325c32SMatt Spinler pel.hostTransmissionState(), 1268e65f4eaSMatt Spinler pel.hmcTransmissionState(), 1278e65f4eaSMatt Spinler pel.plid(), 1288e65f4eaSMatt Spinler pel.getDeconfigFlag(), 1298e65f4eaSMatt Spinler pel.getGuardFlag(), 1308e65f4eaSMatt Spinler getMillisecondsSinceEpoch( 1318e65f4eaSMatt Spinler pel.privateHeader().createTimestamp())}; 1320ff00485SMatt Spinler 133475e574dSMatt Spinler using pelID = LogID::Pel; 134475e574dSMatt Spinler using obmcID = LogID::Obmc; 1350ff00485SMatt Spinler _pelAttributes.emplace( 136475e574dSMatt Spinler LogID(pelID(pel.id()), obmcID(pel.obmcLogID())), 1370ff00485SMatt Spinler attributes); 138b188f78aSMatt Spinler 139b188f78aSMatt Spinler updateRepoStats(attributes, true); 140475e574dSMatt Spinler } 141475e574dSMatt Spinler else 142475e574dSMatt Spinler { 143475e574dSMatt Spinler log<level::ERR>( 144475e574dSMatt Spinler "Found invalid PEL file while restoring. Removing.", 145475e574dSMatt Spinler entry("FILENAME=%s", dirEntry.path().c_str())); 146475e574dSMatt Spinler fs::remove(dirEntry.path()); 147475e574dSMatt Spinler } 148475e574dSMatt Spinler } 14966491c61SPatrick Williams catch (const std::exception& e) 150475e574dSMatt Spinler { 151475e574dSMatt Spinler log<level::ERR>("Hit exception while restoring PEL File", 152475e574dSMatt Spinler entry("FILENAME=%s", dirEntry.path().c_str()), 153475e574dSMatt Spinler entry("ERROR=%s", e.what())); 154475e574dSMatt Spinler } 155475e574dSMatt Spinler } 1561d8835bbSSumit Kumar 1571d8835bbSSumit Kumar // Get size of archive folder 1581d8835bbSSumit Kumar for (auto& dirEntry : fs::directory_iterator(_archivePath)) 1591d8835bbSSumit Kumar { 1601d8835bbSSumit Kumar _archiveSize += getFileDiskSize(dirEntry); 1611d8835bbSSumit Kumar } 16289fa082aSMatt Spinler } 16389fa082aSMatt Spinler 16489fa082aSMatt Spinler std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time) 16589fa082aSMatt Spinler { 16689fa082aSMatt Spinler char name[50]; 16789fa082aSMatt Spinler sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB, 16889fa082aSMatt Spinler time.yearLSB, time.month, time.day, time.hour, time.minutes, 16989fa082aSMatt Spinler time.seconds, time.hundredths, pelID); 17089fa082aSMatt Spinler return std::string{name}; 17189fa082aSMatt Spinler } 17289fa082aSMatt Spinler 17389fa082aSMatt Spinler void Repository::add(std::unique_ptr<PEL>& pel) 17489fa082aSMatt Spinler { 175df43a305SMatt Spinler pel->setHostTransmissionState(TransmissionState::newPEL); 176df43a305SMatt Spinler pel->setHMCTransmissionState(TransmissionState::newPEL); 177df43a305SMatt Spinler 17889fa082aSMatt Spinler auto path = _logPath / getPELFilename(pel->id(), pel->commitTime()); 179ab1b97feSMatt Spinler 180ab1b97feSMatt Spinler write(*(pel.get()), path); 181ab1b97feSMatt Spinler 1828e65f4eaSMatt Spinler PELAttributes attributes{ 1838e65f4eaSMatt Spinler path, 184dd325c32SMatt Spinler getFileDiskSize(path), 185dd325c32SMatt Spinler pel->privateHeader().creatorID(), 186afb1b46fSVijay Lobo pel->userHeader().subsystem(), 187dd325c32SMatt Spinler pel->userHeader().severity(), 188dd325c32SMatt Spinler pel->userHeader().actionFlags(), 189346f99a1SMatt Spinler pel->hostTransmissionState(), 1908e65f4eaSMatt Spinler pel->hmcTransmissionState(), 1918e65f4eaSMatt Spinler pel->plid(), 1928e65f4eaSMatt Spinler pel->getDeconfigFlag(), 1938e65f4eaSMatt Spinler pel->getGuardFlag(), 1948e65f4eaSMatt Spinler getMillisecondsSinceEpoch(pel->privateHeader().createTimestamp())}; 195ab1b97feSMatt Spinler 196ab1b97feSMatt Spinler using pelID = LogID::Pel; 197ab1b97feSMatt Spinler using obmcID = LogID::Obmc; 198ab1b97feSMatt Spinler _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())), 199ab1b97feSMatt Spinler attributes); 200ab1b97feSMatt Spinler 20144893cc9SMatt Spinler _lastPelID = pel->id(); 20244893cc9SMatt Spinler 203b188f78aSMatt Spinler updateRepoStats(attributes, true); 204b188f78aSMatt Spinler 205ab1b97feSMatt Spinler processAddCallbacks(*pel); 206ab1b97feSMatt Spinler } 207ab1b97feSMatt Spinler 208ab1b97feSMatt Spinler void Repository::write(const PEL& pel, const fs::path& path) 209ab1b97feSMatt Spinler { 21089fa082aSMatt Spinler std::ofstream file{path, std::ios::binary}; 21189fa082aSMatt Spinler 21289fa082aSMatt Spinler if (!file.good()) 21389fa082aSMatt Spinler { 21489fa082aSMatt Spinler // If this fails, the filesystem is probably full so it isn't like 21589fa082aSMatt Spinler // we could successfully create yet another error log here. 21689fa082aSMatt Spinler auto e = errno; 21789fa082aSMatt Spinler fs::remove(path); 21889fa082aSMatt Spinler log<level::ERR>("Unable to open PEL file for writing", 21989fa082aSMatt Spinler entry("ERRNO=%d", e), entry("PATH=%s", path.c_str())); 22089fa082aSMatt Spinler throw file_error::Open(); 22189fa082aSMatt Spinler } 22289fa082aSMatt Spinler 223ab1b97feSMatt Spinler auto data = pel.data(); 22489fa082aSMatt Spinler file.write(reinterpret_cast<const char*>(data.data()), data.size()); 22589fa082aSMatt Spinler 22689fa082aSMatt Spinler if (file.fail()) 22789fa082aSMatt Spinler { 22889fa082aSMatt Spinler // Same note as above about not being able to create an error log 22989fa082aSMatt Spinler // for this case even if we wanted. 23089fa082aSMatt Spinler auto e = errno; 23189fa082aSMatt Spinler file.close(); 23289fa082aSMatt Spinler fs::remove(path); 23389fa082aSMatt Spinler log<level::ERR>("Unable to write PEL file", entry("ERRNO=%d", e), 23489fa082aSMatt Spinler entry("PATH=%s", path.c_str())); 23589fa082aSMatt Spinler throw file_error::Write(); 23689fa082aSMatt Spinler } 237475e574dSMatt Spinler } 238475e574dSMatt Spinler 23952602e35SMatt Spinler std::optional<Repository::LogID> Repository::remove(const LogID& id) 240475e574dSMatt Spinler { 241475e574dSMatt Spinler auto pel = findPEL(id); 242ff6b598bSPatrick Williams if (pel == _pelAttributes.end()) 243475e574dSMatt Spinler { 244ff6b598bSPatrick Williams return std::nullopt; 245ff6b598bSPatrick Williams } 246ff6b598bSPatrick Williams 247ff6b598bSPatrick Williams LogID actualID = pel->first; 248b188f78aSMatt Spinler updateRepoStats(pel->second, false); 249b188f78aSMatt Spinler 2505f5352e5SMatt Spinler log<level::DEBUG>("Removing PEL from repository", 251ff6b598bSPatrick Williams entry("PEL_ID=0x%X", actualID.pelID.id), 252ff6b598bSPatrick Williams entry("OBMC_LOG_ID=%d", actualID.obmcID.id)); 2531d8835bbSSumit Kumar 2541d8835bbSSumit Kumar if (fs::exists(pel->second.path)) 2551d8835bbSSumit Kumar { 2561d8835bbSSumit Kumar // Check for existense of new archive folder 2571d8835bbSSumit Kumar if (!fs::exists(_archivePath)) 2581d8835bbSSumit Kumar { 2591d8835bbSSumit Kumar fs::create_directories(_archivePath); 2601d8835bbSSumit Kumar } 2611d8835bbSSumit Kumar 2621d8835bbSSumit Kumar // Move log file to archive folder 2631d8835bbSSumit Kumar auto fileName = _archivePath / pel->second.path.filename(); 2641d8835bbSSumit Kumar fs::rename(pel->second.path, fileName); 2651d8835bbSSumit Kumar 2661d8835bbSSumit Kumar // Update size of file 2671d8835bbSSumit Kumar _archiveSize += getFileDiskSize(fileName); 2681d8835bbSSumit Kumar } 2691d8835bbSSumit Kumar 2700ff00485SMatt Spinler _pelAttributes.erase(pel); 271421f6531SMatt Spinler 272ff6b598bSPatrick Williams processDeleteCallbacks(actualID.pelID.id); 27352602e35SMatt Spinler 27452602e35SMatt Spinler return actualID; 27589fa082aSMatt Spinler } 27689fa082aSMatt Spinler 2772813f36dSMatt Spinler std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id) 2782813f36dSMatt Spinler { 2792813f36dSMatt Spinler auto pel = findPEL(id); 2800ff00485SMatt Spinler if (pel != _pelAttributes.end()) 2812813f36dSMatt Spinler { 2820ff00485SMatt Spinler std::ifstream file{pel->second.path.c_str()}; 2832813f36dSMatt Spinler if (!file.good()) 2842813f36dSMatt Spinler { 2852813f36dSMatt Spinler auto e = errno; 2862813f36dSMatt Spinler log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e), 2870ff00485SMatt Spinler entry("PATH=%s", pel->second.path.c_str())); 2882813f36dSMatt Spinler throw file_error::Open(); 2892813f36dSMatt Spinler } 2902813f36dSMatt Spinler 2912813f36dSMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 2922813f36dSMatt Spinler std::istreambuf_iterator<char>()}; 2932813f36dSMatt Spinler return data; 2942813f36dSMatt Spinler } 2952813f36dSMatt Spinler 2962813f36dSMatt Spinler return std::nullopt; 2972813f36dSMatt Spinler } 2982813f36dSMatt Spinler 2996d51224bSMatt Spinler std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id) 3006d51224bSMatt Spinler { 3016d51224bSMatt Spinler auto pel = findPEL(id); 3026d51224bSMatt Spinler if (pel != _pelAttributes.end()) 3036d51224bSMatt Spinler { 30432a6df6cSMatt Spinler int fd = open(pel->second.path.c_str(), O_RDONLY | O_NONBLOCK); 30532a6df6cSMatt Spinler if (fd == -1) 3066d51224bSMatt Spinler { 3076d51224bSMatt Spinler auto e = errno; 3086d51224bSMatt Spinler log<level::ERR>("Unable to open PEL File", entry("ERRNO=%d", e), 3096d51224bSMatt Spinler entry("PATH=%s", pel->second.path.c_str())); 3106d51224bSMatt Spinler throw file_error::Open(); 3116d51224bSMatt Spinler } 3126d51224bSMatt Spinler 3136d51224bSMatt Spinler // Must leave the file open here. It will be closed by sdbusplus 3146d51224bSMatt Spinler // when it sends it back over D-Bus. 31532a6df6cSMatt Spinler return fd; 3166d51224bSMatt Spinler } 3176d51224bSMatt Spinler return std::nullopt; 3186d51224bSMatt Spinler } 3196d51224bSMatt Spinler 3201ea78801SMatt Spinler void Repository::for_each(ForEachFunc func) const 3211ea78801SMatt Spinler { 3220ff00485SMatt Spinler for (const auto& [id, attributes] : _pelAttributes) 3231ea78801SMatt Spinler { 3240ff00485SMatt Spinler std::ifstream file{attributes.path}; 3251ea78801SMatt Spinler 3261ea78801SMatt Spinler if (!file.good()) 3271ea78801SMatt Spinler { 3281ea78801SMatt Spinler auto e = errno; 3291ea78801SMatt Spinler log<level::ERR>("Repository::for_each: Unable to open PEL file", 3301ea78801SMatt Spinler entry("ERRNO=%d", e), 3310ff00485SMatt Spinler entry("PATH=%s", attributes.path.c_str())); 3321ea78801SMatt Spinler continue; 3331ea78801SMatt Spinler } 3341ea78801SMatt Spinler 3351ea78801SMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 3361ea78801SMatt Spinler std::istreambuf_iterator<char>()}; 3371ea78801SMatt Spinler file.close(); 3381ea78801SMatt Spinler 3391ea78801SMatt Spinler PEL pel{data}; 3401ea78801SMatt Spinler 3411ea78801SMatt Spinler try 3421ea78801SMatt Spinler { 3431ea78801SMatt Spinler if (func(pel)) 3441ea78801SMatt Spinler { 3451ea78801SMatt Spinler break; 3461ea78801SMatt Spinler } 3471ea78801SMatt Spinler } 34866491c61SPatrick Williams catch (const std::exception& e) 3491ea78801SMatt Spinler { 3501ea78801SMatt Spinler log<level::ERR>("Repository::for_each function exception", 3511ea78801SMatt Spinler entry("ERROR=%s", e.what())); 3521ea78801SMatt Spinler } 3531ea78801SMatt Spinler } 3541ea78801SMatt Spinler } 3551ea78801SMatt Spinler 356421f6531SMatt Spinler void Repository::processAddCallbacks(const PEL& pel) const 357421f6531SMatt Spinler { 358421f6531SMatt Spinler for (auto& [name, func] : _addSubscriptions) 359421f6531SMatt Spinler { 360421f6531SMatt Spinler try 361421f6531SMatt Spinler { 362421f6531SMatt Spinler func(pel); 363421f6531SMatt Spinler } 36466491c61SPatrick Williams catch (const std::exception& e) 365421f6531SMatt Spinler { 366421f6531SMatt Spinler log<level::ERR>("PEL Repository add callback exception", 367421f6531SMatt Spinler entry("NAME=%s", name.c_str()), 368421f6531SMatt Spinler entry("ERROR=%s", e.what())); 369421f6531SMatt Spinler } 370421f6531SMatt Spinler } 371421f6531SMatt Spinler } 372421f6531SMatt Spinler 373421f6531SMatt Spinler void Repository::processDeleteCallbacks(uint32_t id) const 374421f6531SMatt Spinler { 375421f6531SMatt Spinler for (auto& [name, func] : _deleteSubscriptions) 376421f6531SMatt Spinler { 377421f6531SMatt Spinler try 378421f6531SMatt Spinler { 379421f6531SMatt Spinler func(id); 380421f6531SMatt Spinler } 38166491c61SPatrick Williams catch (const std::exception& e) 382421f6531SMatt Spinler { 383421f6531SMatt Spinler log<level::ERR>("PEL Repository delete callback exception", 384421f6531SMatt Spinler entry("NAME=%s", name.c_str()), 385421f6531SMatt Spinler entry("ERROR=%s", e.what())); 386421f6531SMatt Spinler } 387421f6531SMatt Spinler } 388421f6531SMatt Spinler } 389421f6531SMatt Spinler 3900ff00485SMatt Spinler std::optional<std::reference_wrapper<const Repository::PELAttributes>> 3910ff00485SMatt Spinler Repository::getPELAttributes(const LogID& id) const 3920ff00485SMatt Spinler { 3930ff00485SMatt Spinler auto pel = findPEL(id); 3940ff00485SMatt Spinler if (pel != _pelAttributes.end()) 3950ff00485SMatt Spinler { 3960ff00485SMatt Spinler return pel->second; 3970ff00485SMatt Spinler } 3980ff00485SMatt Spinler 3990ff00485SMatt Spinler return std::nullopt; 4000ff00485SMatt Spinler } 4010ff00485SMatt Spinler 40229d18c11SMatt Spinler void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state) 40329d18c11SMatt Spinler { 40429d18c11SMatt Spinler LogID id{LogID::Pel{pelID}}; 40529d18c11SMatt Spinler auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 40629d18c11SMatt Spinler [&id](const auto& a) { return a.first == id; }); 40729d18c11SMatt Spinler 40829d18c11SMatt Spinler if ((attr != _pelAttributes.end()) && (attr->second.hostState != state)) 40929d18c11SMatt Spinler { 41029d18c11SMatt Spinler PELUpdateFunc func = [state](PEL& pel) { 41129d18c11SMatt Spinler pel.setHostTransmissionState(state); 412*d0ccda3cSMatt Spinler return true; 41329d18c11SMatt Spinler }; 41429d18c11SMatt Spinler 41529d18c11SMatt Spinler try 41629d18c11SMatt Spinler { 41729d18c11SMatt Spinler updatePEL(attr->second.path, func); 41829d18c11SMatt Spinler } 41966491c61SPatrick Williams catch (const std::exception& e) 42029d18c11SMatt Spinler { 42129d18c11SMatt Spinler log<level::ERR>("Unable to update PEL host transmission state", 42229d18c11SMatt Spinler entry("PATH=%s", attr->second.path.c_str()), 42329d18c11SMatt Spinler entry("ERROR=%s", e.what())); 42429d18c11SMatt Spinler } 42529d18c11SMatt Spinler } 42629d18c11SMatt Spinler } 42729d18c11SMatt Spinler 42829d18c11SMatt Spinler void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state) 42929d18c11SMatt Spinler { 43029d18c11SMatt Spinler LogID id{LogID::Pel{pelID}}; 43129d18c11SMatt Spinler auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 43229d18c11SMatt Spinler [&id](const auto& a) { return a.first == id; }); 43329d18c11SMatt Spinler 43429d18c11SMatt Spinler if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state)) 43529d18c11SMatt Spinler { 43629d18c11SMatt Spinler PELUpdateFunc func = [state](PEL& pel) { 43729d18c11SMatt Spinler pel.setHMCTransmissionState(state); 438*d0ccda3cSMatt Spinler return true; 43929d18c11SMatt Spinler }; 44029d18c11SMatt Spinler 44129d18c11SMatt Spinler try 44229d18c11SMatt Spinler { 44329d18c11SMatt Spinler updatePEL(attr->second.path, func); 44429d18c11SMatt Spinler } 44566491c61SPatrick Williams catch (const std::exception& e) 44629d18c11SMatt Spinler { 44729d18c11SMatt Spinler log<level::ERR>("Unable to update PEL HMC transmission state", 44829d18c11SMatt Spinler entry("PATH=%s", attr->second.path.c_str()), 44929d18c11SMatt Spinler entry("ERROR=%s", e.what())); 45029d18c11SMatt Spinler } 45129d18c11SMatt Spinler } 45229d18c11SMatt Spinler } 45329d18c11SMatt Spinler 45429d18c11SMatt Spinler void Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc) 45529d18c11SMatt Spinler { 45629d18c11SMatt Spinler std::ifstream file{path}; 45729d18c11SMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 45829d18c11SMatt Spinler std::istreambuf_iterator<char>()}; 45929d18c11SMatt Spinler file.close(); 46029d18c11SMatt Spinler 46129d18c11SMatt Spinler PEL pel{data}; 46229d18c11SMatt Spinler 46329d18c11SMatt Spinler if (pel.valid()) 46429d18c11SMatt Spinler { 465*d0ccda3cSMatt Spinler if (updateFunc(pel)) 466*d0ccda3cSMatt Spinler { 467*d0ccda3cSMatt Spinler // Three attribute fields can change post creation from 468*d0ccda3cSMatt Spinler // an updatePEL call: 469*d0ccda3cSMatt Spinler // - hmcTransmissionState - When HMC acks a PEL 470*d0ccda3cSMatt Spinler // - hostTransmissionState - When host acks a PEL 471*d0ccda3cSMatt Spinler // - deconfig flag - Can be cleared for PELs that call out 472*d0ccda3cSMatt Spinler // hotplugged FRUs. 473*d0ccda3cSMatt Spinler // Make sure they're up to date. 474*d0ccda3cSMatt Spinler LogID id{LogID::Pel(pel.id())}; 475*d0ccda3cSMatt Spinler auto attr = 476*d0ccda3cSMatt Spinler std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 477*d0ccda3cSMatt Spinler [&id](const auto& a) { return a.first == id; }); 478*d0ccda3cSMatt Spinler if (attr != _pelAttributes.end()) 479*d0ccda3cSMatt Spinler { 480*d0ccda3cSMatt Spinler attr->second.hmcState = pel.hmcTransmissionState(); 481*d0ccda3cSMatt Spinler attr->second.hostState = pel.hostTransmissionState(); 482*d0ccda3cSMatt Spinler attr->second.deconfig = pel.getDeconfigFlag(); 483*d0ccda3cSMatt Spinler } 48429d18c11SMatt Spinler 48529d18c11SMatt Spinler write(pel, path); 48629d18c11SMatt Spinler } 487*d0ccda3cSMatt Spinler } 48829d18c11SMatt Spinler else 48929d18c11SMatt Spinler { 49029d18c11SMatt Spinler throw std::runtime_error( 49129d18c11SMatt Spinler "Unable to read a valid PEL when trying to update it"); 49229d18c11SMatt Spinler } 49329d18c11SMatt Spinler } 49429d18c11SMatt Spinler 495b188f78aSMatt Spinler bool Repository::isServiceableSev(const PELAttributes& pel) 496b188f78aSMatt Spinler { 497b188f78aSMatt Spinler auto sevType = static_cast<SeverityType>(pel.severity & 0xF0); 4982544b419SPatrick Williams auto sevPVEntry = pel_values::findByValue(pel.severity, 4992544b419SPatrick Williams pel_values::severityValues); 500b188f78aSMatt Spinler std::string sevName = std::get<pel_values::registryNamePos>(*sevPVEntry); 501b188f78aSMatt Spinler 502b188f78aSMatt Spinler bool check1 = (sevType == SeverityType::predictive) || 503b188f78aSMatt Spinler (sevType == SeverityType::unrecoverable) || 504b188f78aSMatt Spinler (sevType == SeverityType::critical); 505b188f78aSMatt Spinler 506b188f78aSMatt Spinler bool check2 = ((sevType == SeverityType::recovered) || 507b188f78aSMatt Spinler (sevName == "symptom_recovered")) && 508b188f78aSMatt Spinler !pel.actionFlags.test(hiddenFlagBit); 509b188f78aSMatt Spinler 510b188f78aSMatt Spinler bool check3 = (sevName == "symptom_predictive") || 511b188f78aSMatt Spinler (sevName == "symptom_unrecoverable") || 512b188f78aSMatt Spinler (sevName == "symptom_critical"); 513b188f78aSMatt Spinler 514b188f78aSMatt Spinler return check1 || check2 || check3; 515b188f78aSMatt Spinler } 516b188f78aSMatt Spinler 517b188f78aSMatt Spinler void Repository::updateRepoStats(const PELAttributes& pel, bool pelAdded) 518b188f78aSMatt Spinler { 519b188f78aSMatt Spinler auto isServiceable = Repository::isServiceableSev(pel); 520b188f78aSMatt Spinler auto bmcPEL = CreatorID::openBMC == static_cast<CreatorID>(pel.creator); 521b188f78aSMatt Spinler 522b188f78aSMatt Spinler auto adjustSize = [pelAdded, &pel](auto& runningSize) { 523b188f78aSMatt Spinler if (pelAdded) 524b188f78aSMatt Spinler { 525b188f78aSMatt Spinler runningSize += pel.sizeOnDisk; 526b188f78aSMatt Spinler } 527b188f78aSMatt Spinler else 528b188f78aSMatt Spinler { 529b188f78aSMatt Spinler runningSize = std::max(static_cast<int64_t>(runningSize) - 530b188f78aSMatt Spinler static_cast<int64_t>(pel.sizeOnDisk), 531b188f78aSMatt Spinler static_cast<int64_t>(0)); 532b188f78aSMatt Spinler } 533b188f78aSMatt Spinler }; 534b188f78aSMatt Spinler 535b188f78aSMatt Spinler adjustSize(_sizes.total); 536b188f78aSMatt Spinler 537b188f78aSMatt Spinler if (bmcPEL) 538b188f78aSMatt Spinler { 539b188f78aSMatt Spinler adjustSize(_sizes.bmc); 540b188f78aSMatt Spinler if (isServiceable) 541b188f78aSMatt Spinler { 542b188f78aSMatt Spinler adjustSize(_sizes.bmcServiceable); 543b188f78aSMatt Spinler } 544b188f78aSMatt Spinler else 545b188f78aSMatt Spinler { 546b188f78aSMatt Spinler adjustSize(_sizes.bmcInfo); 547b188f78aSMatt Spinler } 548b188f78aSMatt Spinler } 549b188f78aSMatt Spinler else 550b188f78aSMatt Spinler { 551b188f78aSMatt Spinler adjustSize(_sizes.nonBMC); 552b188f78aSMatt Spinler if (isServiceable) 553b188f78aSMatt Spinler { 554b188f78aSMatt Spinler adjustSize(_sizes.nonBMCServiceable); 555b188f78aSMatt Spinler } 556b188f78aSMatt Spinler else 557b188f78aSMatt Spinler { 558b188f78aSMatt Spinler adjustSize(_sizes.nonBMCInfo); 559b188f78aSMatt Spinler } 560b188f78aSMatt Spinler } 561b188f78aSMatt Spinler } 562b188f78aSMatt Spinler 563c296692bSSumit Kumar bool Repository::sizeWarning() 5647e727a39SMatt Spinler { 565c296692bSSumit Kumar std::error_code ec; 566c296692bSSumit Kumar 5671d8835bbSSumit Kumar if ((_archiveSize > 0) && ((_sizes.total + _archiveSize) > 5681d8835bbSSumit Kumar ((_maxRepoSize * warningPercentage) / 100))) 5691d8835bbSSumit Kumar { 5701d8835bbSSumit Kumar log<level::INFO>( 5711d8835bbSSumit Kumar "Repository::sizeWarning function:Deleting the files in archive"); 5721d8835bbSSumit Kumar 573c296692bSSumit Kumar for (const auto& dirEntry : fs::directory_iterator(_archivePath)) 5741d8835bbSSumit Kumar { 575c296692bSSumit Kumar fs::remove(dirEntry.path(), ec); 576c296692bSSumit Kumar if (ec) 577c296692bSSumit Kumar { 578c296692bSSumit Kumar log<level::INFO>( 579c296692bSSumit Kumar "Repository::sizeWarning function:Could not delete " 580c296692bSSumit Kumar "a file in PEL archive", 581c296692bSSumit Kumar entry("FILENAME=%s", dirEntry.path().c_str())); 5821d8835bbSSumit Kumar } 5831d8835bbSSumit Kumar } 5841d8835bbSSumit Kumar 585c296692bSSumit Kumar _archiveSize = 0; 586c296692bSSumit Kumar } 587c296692bSSumit Kumar 5887e727a39SMatt Spinler return (_sizes.total > (_maxRepoSize * warningPercentage / 100)) || 5897e727a39SMatt Spinler (_pelAttributes.size() > _maxNumPELs); 5907e727a39SMatt Spinler } 5917e727a39SMatt Spinler 592b0a8df5bSMatt Spinler std::vector<Repository::AttributesReference> 593b0a8df5bSMatt Spinler Repository::getAllPELAttributes(SortOrder order) const 594b0a8df5bSMatt Spinler { 595b0a8df5bSMatt Spinler std::vector<Repository::AttributesReference> attributes; 596b0a8df5bSMatt Spinler 597ac1ba3f2SPatrick Williams std::for_each(_pelAttributes.begin(), _pelAttributes.end(), 598ac1ba3f2SPatrick Williams [&attributes](auto& pelEntry) { 599ac1ba3f2SPatrick Williams attributes.push_back(pelEntry); 600ac1ba3f2SPatrick Williams }); 601b0a8df5bSMatt Spinler 602b0a8df5bSMatt Spinler std::sort(attributes.begin(), attributes.end(), 603b0a8df5bSMatt Spinler [order](const auto& left, const auto& right) { 604b0a8df5bSMatt Spinler if (order == SortOrder::ascending) 605b0a8df5bSMatt Spinler { 606b0a8df5bSMatt Spinler return left.get().second.path < right.get().second.path; 607b0a8df5bSMatt Spinler } 608b0a8df5bSMatt Spinler return left.get().second.path > right.get().second.path; 609b0a8df5bSMatt Spinler }); 610b0a8df5bSMatt Spinler 611b0a8df5bSMatt Spinler return attributes; 612b0a8df5bSMatt Spinler } 613b0a8df5bSMatt Spinler 614027bf285SSumit Kumar std::vector<uint32_t> 615027bf285SSumit Kumar Repository::prune(const std::vector<uint32_t>& idsWithHwIsoEntry) 616b0a8df5bSMatt Spinler { 617b0a8df5bSMatt Spinler std::vector<uint32_t> obmcLogIDs; 618b0a8df5bSMatt Spinler std::string msg = "Pruning PEL repository that takes up " + 619b0a8df5bSMatt Spinler std::to_string(_sizes.total) + " bytes and has " + 620b0a8df5bSMatt Spinler std::to_string(_pelAttributes.size()) + " PELs"; 621b0a8df5bSMatt Spinler log<level::INFO>(msg.c_str()); 622b0a8df5bSMatt Spinler 623b0a8df5bSMatt Spinler // Set up the 5 functions to check if the PEL category 624b0a8df5bSMatt Spinler // is still over its limits. 625b0a8df5bSMatt Spinler 626b0a8df5bSMatt Spinler // BMC informational PELs should only take up 15% 627b0a8df5bSMatt Spinler IsOverLimitFunc overBMCInfoLimit = [this]() { 628b0a8df5bSMatt Spinler return _sizes.bmcInfo > _maxRepoSize * 15 / 100; 629b0a8df5bSMatt Spinler }; 630b0a8df5bSMatt Spinler 631b0a8df5bSMatt Spinler // BMC non informational PELs should only take up 30% 632b0a8df5bSMatt Spinler IsOverLimitFunc overBMCNonInfoLimit = [this]() { 633b0a8df5bSMatt Spinler return _sizes.bmcServiceable > _maxRepoSize * 30 / 100; 634b0a8df5bSMatt Spinler }; 635b0a8df5bSMatt Spinler 636b0a8df5bSMatt Spinler // Non BMC informational PELs should only take up 15% 637b0a8df5bSMatt Spinler IsOverLimitFunc overNonBMCInfoLimit = [this]() { 638b0a8df5bSMatt Spinler return _sizes.nonBMCInfo > _maxRepoSize * 15 / 100; 639b0a8df5bSMatt Spinler }; 640b0a8df5bSMatt Spinler 641b0a8df5bSMatt Spinler // Non BMC non informational PELs should only take up 15% 642b0a8df5bSMatt Spinler IsOverLimitFunc overNonBMCNonInfoLimit = [this]() { 643b0a8df5bSMatt Spinler return _sizes.nonBMCServiceable > _maxRepoSize * 30 / 100; 644b0a8df5bSMatt Spinler }; 645b0a8df5bSMatt Spinler 646b0a8df5bSMatt Spinler // Bring the total number of PELs down to 80% of the max 647b0a8df5bSMatt Spinler IsOverLimitFunc tooManyPELsLimit = [this]() { 648b0a8df5bSMatt Spinler return _pelAttributes.size() > _maxNumPELs * 80 / 100; 649b0a8df5bSMatt Spinler }; 650b0a8df5bSMatt Spinler 651b0a8df5bSMatt Spinler // Set up the functions to determine which category a PEL is in. 652b0a8df5bSMatt Spinler // TODO: Return false in these functions if a PEL caused a guard record. 653b0a8df5bSMatt Spinler 654b0a8df5bSMatt Spinler // A BMC informational PEL 655b0a8df5bSMatt Spinler IsPELTypeFunc isBMCInfo = [](const PELAttributes& pel) { 656b0a8df5bSMatt Spinler return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) && 657b0a8df5bSMatt Spinler !Repository::isServiceableSev(pel); 658b0a8df5bSMatt Spinler }; 659b0a8df5bSMatt Spinler 660b0a8df5bSMatt Spinler // A BMC non informational PEL 661b0a8df5bSMatt Spinler IsPELTypeFunc isBMCNonInfo = [](const PELAttributes& pel) { 662b0a8df5bSMatt Spinler return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) && 663b0a8df5bSMatt Spinler Repository::isServiceableSev(pel); 664b0a8df5bSMatt Spinler }; 665b0a8df5bSMatt Spinler 666b0a8df5bSMatt Spinler // A non BMC informational PEL 667b0a8df5bSMatt Spinler IsPELTypeFunc isNonBMCInfo = [](const PELAttributes& pel) { 668b0a8df5bSMatt Spinler return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) && 669b0a8df5bSMatt Spinler !Repository::isServiceableSev(pel); 670b0a8df5bSMatt Spinler }; 671b0a8df5bSMatt Spinler 672b0a8df5bSMatt Spinler // A non BMC non informational PEL 673b0a8df5bSMatt Spinler IsPELTypeFunc isNonBMCNonInfo = [](const PELAttributes& pel) { 674b0a8df5bSMatt Spinler return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) && 675b0a8df5bSMatt Spinler Repository::isServiceableSev(pel); 676b0a8df5bSMatt Spinler }; 677b0a8df5bSMatt Spinler 678b0a8df5bSMatt Spinler // When counting PELs, count every PEL 679d26fa3e7SPatrick Williams IsPELTypeFunc isAnyPEL = [](const PELAttributes& /*pel*/) { return true; }; 680b0a8df5bSMatt Spinler 681b0a8df5bSMatt Spinler // Check all 4 categories, which will result in at most 90% 682b0a8df5bSMatt Spinler // usage (15 + 30 + 15 + 30). 683027bf285SSumit Kumar removePELs(overBMCInfoLimit, isBMCInfo, idsWithHwIsoEntry, obmcLogIDs); 684027bf285SSumit Kumar removePELs(overBMCNonInfoLimit, isBMCNonInfo, idsWithHwIsoEntry, 685027bf285SSumit Kumar obmcLogIDs); 686027bf285SSumit Kumar removePELs(overNonBMCInfoLimit, isNonBMCInfo, idsWithHwIsoEntry, 687027bf285SSumit Kumar obmcLogIDs); 688027bf285SSumit Kumar removePELs(overNonBMCNonInfoLimit, isNonBMCNonInfo, idsWithHwIsoEntry, 689027bf285SSumit Kumar obmcLogIDs); 690b0a8df5bSMatt Spinler 691b0a8df5bSMatt Spinler // After the above pruning check if there are still too many PELs, 692b0a8df5bSMatt Spinler // which can happen depending on PEL sizes. 693b0a8df5bSMatt Spinler if (_pelAttributes.size() > _maxNumPELs) 694b0a8df5bSMatt Spinler { 695027bf285SSumit Kumar removePELs(tooManyPELsLimit, isAnyPEL, idsWithHwIsoEntry, obmcLogIDs); 696b0a8df5bSMatt Spinler } 697b0a8df5bSMatt Spinler 698b0a8df5bSMatt Spinler if (!obmcLogIDs.empty()) 699b0a8df5bSMatt Spinler { 70045796e82SMatt Spinler std::string m = "Number of PELs removed to save space: " + 701b0a8df5bSMatt Spinler std::to_string(obmcLogIDs.size()); 70245796e82SMatt Spinler log<level::INFO>(m.c_str()); 703b0a8df5bSMatt Spinler } 704b0a8df5bSMatt Spinler 705b0a8df5bSMatt Spinler return obmcLogIDs; 706b0a8df5bSMatt Spinler } 707b0a8df5bSMatt Spinler 70845796e82SMatt Spinler void Repository::removePELs(const IsOverLimitFunc& isOverLimit, 70945796e82SMatt Spinler const IsPELTypeFunc& isPELType, 710027bf285SSumit Kumar const std::vector<uint32_t>& idsWithHwIsoEntry, 711b0a8df5bSMatt Spinler std::vector<uint32_t>& removedBMCLogIDs) 712b0a8df5bSMatt Spinler { 713b0a8df5bSMatt Spinler if (!isOverLimit()) 714b0a8df5bSMatt Spinler { 715b0a8df5bSMatt Spinler return; 716b0a8df5bSMatt Spinler } 717b0a8df5bSMatt Spinler 718b0a8df5bSMatt Spinler auto attributes = getAllPELAttributes(SortOrder::ascending); 719b0a8df5bSMatt Spinler 720b0a8df5bSMatt Spinler // Make 4 passes on the PELs, stopping as soon as isOverLimit 721b0a8df5bSMatt Spinler // returns false. 722b0a8df5bSMatt Spinler // Pass 1: only delete HMC acked PELs 723b0a8df5bSMatt Spinler // Pass 2: only delete OS acked PELs 724b0a8df5bSMatt Spinler // Pass 3: only delete PHYP sent PELs 725b0a8df5bSMatt Spinler // Pass 4: delete all PELs 726b0a8df5bSMatt Spinler static const std::vector<std::function<bool(const PELAttributes& pel)>> 727b0a8df5bSMatt Spinler stateChecks{[](const auto& pel) { 728b0a8df5bSMatt Spinler return pel.hmcState == TransmissionState::acked; 729b0a8df5bSMatt Spinler }, 730b0a8df5bSMatt Spinler 731b0a8df5bSMatt Spinler [](const auto& pel) { 732b0a8df5bSMatt Spinler return pel.hostState == TransmissionState::acked; 733b0a8df5bSMatt Spinler }, 734b0a8df5bSMatt Spinler 735b0a8df5bSMatt Spinler [](const auto& pel) { 736b0a8df5bSMatt Spinler return pel.hostState == TransmissionState::sent; 737b0a8df5bSMatt Spinler }, 738b0a8df5bSMatt Spinler 739d26fa3e7SPatrick Williams [](const auto& /*pel*/) { return true; }}; 740b0a8df5bSMatt Spinler 741b0a8df5bSMatt Spinler for (const auto& stateCheck : stateChecks) 742b0a8df5bSMatt Spinler { 743b0a8df5bSMatt Spinler for (auto it = attributes.begin(); it != attributes.end();) 744b0a8df5bSMatt Spinler { 745b0a8df5bSMatt Spinler const auto& pel = it->get(); 746b0a8df5bSMatt Spinler if (isPELType(pel.second) && stateCheck(pel.second)) 747b0a8df5bSMatt Spinler { 748b0a8df5bSMatt Spinler auto removedID = pel.first.obmcID.id; 749027bf285SSumit Kumar 750027bf285SSumit Kumar auto idFound = std::find(idsWithHwIsoEntry.begin(), 751027bf285SSumit Kumar idsWithHwIsoEntry.end(), removedID); 752027bf285SSumit Kumar if (idFound != idsWithHwIsoEntry.end()) 753027bf285SSumit Kumar { 754027bf285SSumit Kumar ++it; 755027bf285SSumit Kumar continue; 756027bf285SSumit Kumar } 757027bf285SSumit Kumar 758b0a8df5bSMatt Spinler remove(pel.first); 759b0a8df5bSMatt Spinler 760b0a8df5bSMatt Spinler removedBMCLogIDs.push_back(removedID); 761b0a8df5bSMatt Spinler 762b0a8df5bSMatt Spinler attributes.erase(it); 763b0a8df5bSMatt Spinler 764b0a8df5bSMatt Spinler if (!isOverLimit()) 765b0a8df5bSMatt Spinler { 766b0a8df5bSMatt Spinler break; 767b0a8df5bSMatt Spinler } 768b0a8df5bSMatt Spinler } 769b0a8df5bSMatt Spinler else 770b0a8df5bSMatt Spinler { 771b0a8df5bSMatt Spinler ++it; 772b0a8df5bSMatt Spinler } 773b0a8df5bSMatt Spinler } 774b0a8df5bSMatt Spinler 775b0a8df5bSMatt Spinler if (!isOverLimit()) 776b0a8df5bSMatt Spinler { 777b0a8df5bSMatt Spinler break; 778b0a8df5bSMatt Spinler } 779b0a8df5bSMatt Spinler } 780b0a8df5bSMatt Spinler } 781b0a8df5bSMatt Spinler 7822ccdcef9SSumit Kumar void Repository::archivePEL(const PEL& pel) 7832ccdcef9SSumit Kumar { 7842ccdcef9SSumit Kumar if (pel.valid()) 7852ccdcef9SSumit Kumar { 7862ccdcef9SSumit Kumar auto path = _archivePath / getPELFilename(pel.id(), pel.commitTime()); 7872ccdcef9SSumit Kumar 7882ccdcef9SSumit Kumar write(pel, path); 7892ccdcef9SSumit Kumar 7902ccdcef9SSumit Kumar _archiveSize += getFileDiskSize(path); 7912ccdcef9SSumit Kumar } 7922ccdcef9SSumit Kumar } 7932ccdcef9SSumit Kumar 79489fa082aSMatt Spinler } // namespace pels 79589fa082aSMatt Spinler } // namespace openpower 796