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 18dd325c32SMatt Spinler #include <sys/stat.h> 19dd325c32SMatt Spinler 2089fa082aSMatt Spinler #include <phosphor-logging/log.hpp> 2189fa082aSMatt Spinler #include <xyz/openbmc_project/Common/File/error.hpp> 2289fa082aSMatt Spinler 23*2544b419SPatrick Williams #include <fstream> 24*2544b419SPatrick Williams 2589fa082aSMatt Spinler namespace openpower 2689fa082aSMatt Spinler { 2789fa082aSMatt Spinler namespace pels 2889fa082aSMatt Spinler { 2989fa082aSMatt Spinler 3089fa082aSMatt Spinler namespace fs = std::filesystem; 3189fa082aSMatt Spinler using namespace phosphor::logging; 3289fa082aSMatt Spinler namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error; 3389fa082aSMatt Spinler 347e727a39SMatt Spinler constexpr size_t warningPercentage = 95; 357e727a39SMatt Spinler 36dd325c32SMatt Spinler /** 37dd325c32SMatt Spinler * @brief Returns the amount of space the file uses on disk. 38dd325c32SMatt Spinler * 39dd325c32SMatt Spinler * This is different than just the regular size of the file. 40dd325c32SMatt Spinler * 41dd325c32SMatt Spinler * @param[in] file - The file to get the size of 42dd325c32SMatt Spinler * 43dd325c32SMatt Spinler * @return size_t The disk space the file uses 44dd325c32SMatt Spinler */ 45dd325c32SMatt Spinler size_t getFileDiskSize(const std::filesystem::path& file) 46dd325c32SMatt Spinler { 47dd325c32SMatt Spinler constexpr size_t statBlockSize = 512; 48dd325c32SMatt Spinler struct stat statData; 49dd325c32SMatt Spinler auto rc = stat(file.c_str(), &statData); 50dd325c32SMatt Spinler if (rc != 0) 51dd325c32SMatt Spinler { 52dd325c32SMatt Spinler auto e = errno; 53dd325c32SMatt Spinler std::string msg = "call to stat() failed on " + file.native() + 54dd325c32SMatt Spinler " with errno " + std::to_string(e); 55dd325c32SMatt Spinler log<level::ERR>(msg.c_str()); 56dd325c32SMatt Spinler abort(); 57dd325c32SMatt Spinler } 58dd325c32SMatt Spinler 59dd325c32SMatt Spinler return statData.st_blocks * statBlockSize; 60dd325c32SMatt Spinler } 61dd325c32SMatt Spinler 628d5f3a2bSMatt Spinler Repository::Repository(const std::filesystem::path& basePath, size_t repoSize, 638d5f3a2bSMatt Spinler size_t maxNumPELs) : 648d5f3a2bSMatt Spinler _logPath(basePath / "logs"), 651d8835bbSSumit Kumar _maxRepoSize(repoSize), _maxNumPELs(maxNumPELs), 661d8835bbSSumit Kumar _archivePath(basePath / "logs" / "archive") 6789fa082aSMatt Spinler { 6889fa082aSMatt Spinler if (!fs::exists(_logPath)) 6989fa082aSMatt Spinler { 7089fa082aSMatt Spinler fs::create_directories(_logPath); 7189fa082aSMatt Spinler } 72475e574dSMatt Spinler 731d8835bbSSumit Kumar if (!fs::exists(_archivePath)) 741d8835bbSSumit Kumar { 751d8835bbSSumit Kumar fs::create_directories(_archivePath); 761d8835bbSSumit Kumar } 771d8835bbSSumit Kumar 78475e574dSMatt Spinler restore(); 79475e574dSMatt Spinler } 80475e574dSMatt Spinler 81475e574dSMatt Spinler void Repository::restore() 82475e574dSMatt Spinler { 83475e574dSMatt Spinler for (auto& dirEntry : fs::directory_iterator(_logPath)) 84475e574dSMatt Spinler { 85475e574dSMatt Spinler try 86475e574dSMatt Spinler { 87475e574dSMatt Spinler if (!fs::is_regular_file(dirEntry.path())) 88475e574dSMatt Spinler { 89475e574dSMatt Spinler continue; 90475e574dSMatt Spinler } 91475e574dSMatt Spinler 92475e574dSMatt Spinler std::ifstream file{dirEntry.path()}; 93475e574dSMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 94475e574dSMatt Spinler std::istreambuf_iterator<char>()}; 95475e574dSMatt Spinler file.close(); 96475e574dSMatt Spinler 9707eefc54SMatt Spinler PEL pel{data}; 98475e574dSMatt Spinler if (pel.valid()) 99475e574dSMatt Spinler { 100a3c12a48SMatt Spinler // If the host hasn't acked it, reset the host state so 101a3c12a48SMatt Spinler // it will get sent up again. 102a3c12a48SMatt Spinler if (pel.hostTransmissionState() == TransmissionState::sent) 103a3c12a48SMatt Spinler { 104a3c12a48SMatt Spinler pel.setHostTransmissionState(TransmissionState::newPEL); 105a3c12a48SMatt Spinler try 106a3c12a48SMatt Spinler { 107a3c12a48SMatt Spinler write(pel, dirEntry.path()); 108a3c12a48SMatt Spinler } 10966491c61SPatrick Williams catch (const std::exception& e) 110a3c12a48SMatt Spinler { 111a3c12a48SMatt Spinler log<level::ERR>( 112a3c12a48SMatt Spinler "Failed to save PEL after updating host state", 113a3c12a48SMatt Spinler entry("PELID=0x%X", pel.id())); 114a3c12a48SMatt Spinler } 115a3c12a48SMatt Spinler } 116a3c12a48SMatt Spinler 117dd325c32SMatt Spinler PELAttributes attributes{dirEntry.path(), 118dd325c32SMatt Spinler getFileDiskSize(dirEntry.path()), 119dd325c32SMatt Spinler pel.privateHeader().creatorID(), 120afb1b46fSVijay Lobo pel.userHeader().subsystem(), 121dd325c32SMatt Spinler pel.userHeader().severity(), 122dd325c32SMatt Spinler pel.userHeader().actionFlags(), 123dd325c32SMatt Spinler pel.hostTransmissionState(), 124dd325c32SMatt Spinler pel.hmcTransmissionState()}; 1250ff00485SMatt Spinler 126475e574dSMatt Spinler using pelID = LogID::Pel; 127475e574dSMatt Spinler using obmcID = LogID::Obmc; 1280ff00485SMatt Spinler _pelAttributes.emplace( 129475e574dSMatt Spinler LogID(pelID(pel.id()), obmcID(pel.obmcLogID())), 1300ff00485SMatt Spinler attributes); 131b188f78aSMatt Spinler 132b188f78aSMatt Spinler updateRepoStats(attributes, true); 133475e574dSMatt Spinler } 134475e574dSMatt Spinler else 135475e574dSMatt Spinler { 136475e574dSMatt Spinler log<level::ERR>( 137475e574dSMatt Spinler "Found invalid PEL file while restoring. Removing.", 138475e574dSMatt Spinler entry("FILENAME=%s", dirEntry.path().c_str())); 139475e574dSMatt Spinler fs::remove(dirEntry.path()); 140475e574dSMatt Spinler } 141475e574dSMatt Spinler } 14266491c61SPatrick Williams catch (const std::exception& e) 143475e574dSMatt Spinler { 144475e574dSMatt Spinler log<level::ERR>("Hit exception while restoring PEL File", 145475e574dSMatt Spinler entry("FILENAME=%s", dirEntry.path().c_str()), 146475e574dSMatt Spinler entry("ERROR=%s", e.what())); 147475e574dSMatt Spinler } 148475e574dSMatt Spinler } 1491d8835bbSSumit Kumar 1501d8835bbSSumit Kumar // Get size of archive folder 1511d8835bbSSumit Kumar for (auto& dirEntry : fs::directory_iterator(_archivePath)) 1521d8835bbSSumit Kumar { 1531d8835bbSSumit Kumar _archiveSize += getFileDiskSize(dirEntry); 1541d8835bbSSumit Kumar } 15589fa082aSMatt Spinler } 15689fa082aSMatt Spinler 15789fa082aSMatt Spinler std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time) 15889fa082aSMatt Spinler { 15989fa082aSMatt Spinler char name[50]; 16089fa082aSMatt Spinler sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB, 16189fa082aSMatt Spinler time.yearLSB, time.month, time.day, time.hour, time.minutes, 16289fa082aSMatt Spinler time.seconds, time.hundredths, pelID); 16389fa082aSMatt Spinler return std::string{name}; 16489fa082aSMatt Spinler } 16589fa082aSMatt Spinler 16689fa082aSMatt Spinler void Repository::add(std::unique_ptr<PEL>& pel) 16789fa082aSMatt Spinler { 168df43a305SMatt Spinler pel->setHostTransmissionState(TransmissionState::newPEL); 169df43a305SMatt Spinler pel->setHMCTransmissionState(TransmissionState::newPEL); 170df43a305SMatt Spinler 17189fa082aSMatt Spinler auto path = _logPath / getPELFilename(pel->id(), pel->commitTime()); 172ab1b97feSMatt Spinler 173ab1b97feSMatt Spinler write(*(pel.get()), path); 174ab1b97feSMatt Spinler 175dd325c32SMatt Spinler PELAttributes attributes{path, 176dd325c32SMatt Spinler getFileDiskSize(path), 177dd325c32SMatt Spinler pel->privateHeader().creatorID(), 178afb1b46fSVijay Lobo pel->userHeader().subsystem(), 179dd325c32SMatt Spinler pel->userHeader().severity(), 180dd325c32SMatt Spinler pel->userHeader().actionFlags(), 181346f99a1SMatt Spinler pel->hostTransmissionState(), 182346f99a1SMatt Spinler pel->hmcTransmissionState()}; 183ab1b97feSMatt Spinler 184ab1b97feSMatt Spinler using pelID = LogID::Pel; 185ab1b97feSMatt Spinler using obmcID = LogID::Obmc; 186ab1b97feSMatt Spinler _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())), 187ab1b97feSMatt Spinler attributes); 188ab1b97feSMatt Spinler 18944893cc9SMatt Spinler _lastPelID = pel->id(); 19044893cc9SMatt Spinler 191b188f78aSMatt Spinler updateRepoStats(attributes, true); 192b188f78aSMatt Spinler 193ab1b97feSMatt Spinler processAddCallbacks(*pel); 194ab1b97feSMatt Spinler } 195ab1b97feSMatt Spinler 196ab1b97feSMatt Spinler void Repository::write(const PEL& pel, const fs::path& path) 197ab1b97feSMatt Spinler { 19889fa082aSMatt Spinler std::ofstream file{path, std::ios::binary}; 19989fa082aSMatt Spinler 20089fa082aSMatt Spinler if (!file.good()) 20189fa082aSMatt Spinler { 20289fa082aSMatt Spinler // If this fails, the filesystem is probably full so it isn't like 20389fa082aSMatt Spinler // we could successfully create yet another error log here. 20489fa082aSMatt Spinler auto e = errno; 20589fa082aSMatt Spinler fs::remove(path); 20689fa082aSMatt Spinler log<level::ERR>("Unable to open PEL file for writing", 20789fa082aSMatt Spinler entry("ERRNO=%d", e), entry("PATH=%s", path.c_str())); 20889fa082aSMatt Spinler throw file_error::Open(); 20989fa082aSMatt Spinler } 21089fa082aSMatt Spinler 211ab1b97feSMatt Spinler auto data = pel.data(); 21289fa082aSMatt Spinler file.write(reinterpret_cast<const char*>(data.data()), data.size()); 21389fa082aSMatt Spinler 21489fa082aSMatt Spinler if (file.fail()) 21589fa082aSMatt Spinler { 21689fa082aSMatt Spinler // Same note as above about not being able to create an error log 21789fa082aSMatt Spinler // for this case even if we wanted. 21889fa082aSMatt Spinler auto e = errno; 21989fa082aSMatt Spinler file.close(); 22089fa082aSMatt Spinler fs::remove(path); 22189fa082aSMatt Spinler log<level::ERR>("Unable to write PEL file", entry("ERRNO=%d", e), 22289fa082aSMatt Spinler entry("PATH=%s", path.c_str())); 22389fa082aSMatt Spinler throw file_error::Write(); 22489fa082aSMatt Spinler } 225475e574dSMatt Spinler } 226475e574dSMatt Spinler 22752602e35SMatt Spinler std::optional<Repository::LogID> Repository::remove(const LogID& id) 228475e574dSMatt Spinler { 229475e574dSMatt Spinler auto pel = findPEL(id); 230ff6b598bSPatrick Williams if (pel == _pelAttributes.end()) 231475e574dSMatt Spinler { 232ff6b598bSPatrick Williams return std::nullopt; 233ff6b598bSPatrick Williams } 234ff6b598bSPatrick Williams 235ff6b598bSPatrick Williams LogID actualID = pel->first; 236b188f78aSMatt Spinler updateRepoStats(pel->second, false); 237b188f78aSMatt Spinler 2385f5352e5SMatt Spinler log<level::DEBUG>("Removing PEL from repository", 239ff6b598bSPatrick Williams entry("PEL_ID=0x%X", actualID.pelID.id), 240ff6b598bSPatrick Williams entry("OBMC_LOG_ID=%d", actualID.obmcID.id)); 2411d8835bbSSumit Kumar 2421d8835bbSSumit Kumar if (fs::exists(pel->second.path)) 2431d8835bbSSumit Kumar { 2441d8835bbSSumit Kumar // Check for existense of new archive folder 2451d8835bbSSumit Kumar if (!fs::exists(_archivePath)) 2461d8835bbSSumit Kumar { 2471d8835bbSSumit Kumar fs::create_directories(_archivePath); 2481d8835bbSSumit Kumar } 2491d8835bbSSumit Kumar 2501d8835bbSSumit Kumar // Move log file to archive folder 2511d8835bbSSumit Kumar auto fileName = _archivePath / pel->second.path.filename(); 2521d8835bbSSumit Kumar fs::rename(pel->second.path, fileName); 2531d8835bbSSumit Kumar 2541d8835bbSSumit Kumar // Update size of file 2551d8835bbSSumit Kumar _archiveSize += getFileDiskSize(fileName); 2561d8835bbSSumit Kumar } 2571d8835bbSSumit Kumar 2580ff00485SMatt Spinler _pelAttributes.erase(pel); 259421f6531SMatt Spinler 260ff6b598bSPatrick Williams processDeleteCallbacks(actualID.pelID.id); 26152602e35SMatt Spinler 26252602e35SMatt Spinler return actualID; 26389fa082aSMatt Spinler } 26489fa082aSMatt Spinler 2652813f36dSMatt Spinler std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id) 2662813f36dSMatt Spinler { 2672813f36dSMatt Spinler auto pel = findPEL(id); 2680ff00485SMatt Spinler if (pel != _pelAttributes.end()) 2692813f36dSMatt Spinler { 2700ff00485SMatt Spinler std::ifstream file{pel->second.path.c_str()}; 2712813f36dSMatt Spinler if (!file.good()) 2722813f36dSMatt Spinler { 2732813f36dSMatt Spinler auto e = errno; 2742813f36dSMatt Spinler log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e), 2750ff00485SMatt Spinler entry("PATH=%s", pel->second.path.c_str())); 2762813f36dSMatt Spinler throw file_error::Open(); 2772813f36dSMatt Spinler } 2782813f36dSMatt Spinler 2792813f36dSMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 2802813f36dSMatt Spinler std::istreambuf_iterator<char>()}; 2812813f36dSMatt Spinler return data; 2822813f36dSMatt Spinler } 2832813f36dSMatt Spinler 2842813f36dSMatt Spinler return std::nullopt; 2852813f36dSMatt Spinler } 2862813f36dSMatt Spinler 2876d51224bSMatt Spinler std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id) 2886d51224bSMatt Spinler { 2896d51224bSMatt Spinler auto pel = findPEL(id); 2906d51224bSMatt Spinler if (pel != _pelAttributes.end()) 2916d51224bSMatt Spinler { 2926d51224bSMatt Spinler FILE* fp = fopen(pel->second.path.c_str(), "rb"); 2936d51224bSMatt Spinler 2946d51224bSMatt Spinler if (fp == nullptr) 2956d51224bSMatt Spinler { 2966d51224bSMatt Spinler auto e = errno; 2976d51224bSMatt Spinler log<level::ERR>("Unable to open PEL File", entry("ERRNO=%d", e), 2986d51224bSMatt Spinler entry("PATH=%s", pel->second.path.c_str())); 2996d51224bSMatt Spinler throw file_error::Open(); 3006d51224bSMatt Spinler } 3016d51224bSMatt Spinler 3026d51224bSMatt Spinler // Must leave the file open here. It will be closed by sdbusplus 3036d51224bSMatt Spinler // when it sends it back over D-Bus. 3046d51224bSMatt Spinler 3056d51224bSMatt Spinler return fileno(fp); 3066d51224bSMatt Spinler } 3076d51224bSMatt Spinler return std::nullopt; 3086d51224bSMatt Spinler } 3096d51224bSMatt Spinler 3101ea78801SMatt Spinler void Repository::for_each(ForEachFunc func) const 3111ea78801SMatt Spinler { 3120ff00485SMatt Spinler for (const auto& [id, attributes] : _pelAttributes) 3131ea78801SMatt Spinler { 3140ff00485SMatt Spinler std::ifstream file{attributes.path}; 3151ea78801SMatt Spinler 3161ea78801SMatt Spinler if (!file.good()) 3171ea78801SMatt Spinler { 3181ea78801SMatt Spinler auto e = errno; 3191ea78801SMatt Spinler log<level::ERR>("Repository::for_each: Unable to open PEL file", 3201ea78801SMatt Spinler entry("ERRNO=%d", e), 3210ff00485SMatt Spinler entry("PATH=%s", attributes.path.c_str())); 3221ea78801SMatt Spinler continue; 3231ea78801SMatt Spinler } 3241ea78801SMatt Spinler 3251ea78801SMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 3261ea78801SMatt Spinler std::istreambuf_iterator<char>()}; 3271ea78801SMatt Spinler file.close(); 3281ea78801SMatt Spinler 3291ea78801SMatt Spinler PEL pel{data}; 3301ea78801SMatt Spinler 3311ea78801SMatt Spinler try 3321ea78801SMatt Spinler { 3331ea78801SMatt Spinler if (func(pel)) 3341ea78801SMatt Spinler { 3351ea78801SMatt Spinler break; 3361ea78801SMatt Spinler } 3371ea78801SMatt Spinler } 33866491c61SPatrick Williams catch (const std::exception& e) 3391ea78801SMatt Spinler { 3401ea78801SMatt Spinler log<level::ERR>("Repository::for_each function exception", 3411ea78801SMatt Spinler entry("ERROR=%s", e.what())); 3421ea78801SMatt Spinler } 3431ea78801SMatt Spinler } 3441ea78801SMatt Spinler } 3451ea78801SMatt Spinler 346421f6531SMatt Spinler void Repository::processAddCallbacks(const PEL& pel) const 347421f6531SMatt Spinler { 348421f6531SMatt Spinler for (auto& [name, func] : _addSubscriptions) 349421f6531SMatt Spinler { 350421f6531SMatt Spinler try 351421f6531SMatt Spinler { 352421f6531SMatt Spinler func(pel); 353421f6531SMatt Spinler } 35466491c61SPatrick Williams catch (const std::exception& e) 355421f6531SMatt Spinler { 356421f6531SMatt Spinler log<level::ERR>("PEL Repository add callback exception", 357421f6531SMatt Spinler entry("NAME=%s", name.c_str()), 358421f6531SMatt Spinler entry("ERROR=%s", e.what())); 359421f6531SMatt Spinler } 360421f6531SMatt Spinler } 361421f6531SMatt Spinler } 362421f6531SMatt Spinler 363421f6531SMatt Spinler void Repository::processDeleteCallbacks(uint32_t id) const 364421f6531SMatt Spinler { 365421f6531SMatt Spinler for (auto& [name, func] : _deleteSubscriptions) 366421f6531SMatt Spinler { 367421f6531SMatt Spinler try 368421f6531SMatt Spinler { 369421f6531SMatt Spinler func(id); 370421f6531SMatt Spinler } 37166491c61SPatrick Williams catch (const std::exception& e) 372421f6531SMatt Spinler { 373421f6531SMatt Spinler log<level::ERR>("PEL Repository delete callback exception", 374421f6531SMatt Spinler entry("NAME=%s", name.c_str()), 375421f6531SMatt Spinler entry("ERROR=%s", e.what())); 376421f6531SMatt Spinler } 377421f6531SMatt Spinler } 378421f6531SMatt Spinler } 379421f6531SMatt Spinler 3800ff00485SMatt Spinler std::optional<std::reference_wrapper<const Repository::PELAttributes>> 3810ff00485SMatt Spinler Repository::getPELAttributes(const LogID& id) const 3820ff00485SMatt Spinler { 3830ff00485SMatt Spinler auto pel = findPEL(id); 3840ff00485SMatt Spinler if (pel != _pelAttributes.end()) 3850ff00485SMatt Spinler { 3860ff00485SMatt Spinler return pel->second; 3870ff00485SMatt Spinler } 3880ff00485SMatt Spinler 3890ff00485SMatt Spinler return std::nullopt; 3900ff00485SMatt Spinler } 3910ff00485SMatt Spinler 39229d18c11SMatt Spinler void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state) 39329d18c11SMatt Spinler { 39429d18c11SMatt Spinler LogID id{LogID::Pel{pelID}}; 39529d18c11SMatt Spinler auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 39629d18c11SMatt Spinler [&id](const auto& a) { return a.first == id; }); 39729d18c11SMatt Spinler 39829d18c11SMatt Spinler if ((attr != _pelAttributes.end()) && (attr->second.hostState != state)) 39929d18c11SMatt Spinler { 40029d18c11SMatt Spinler PELUpdateFunc func = [state](PEL& pel) { 40129d18c11SMatt Spinler pel.setHostTransmissionState(state); 40229d18c11SMatt Spinler }; 40329d18c11SMatt Spinler 40429d18c11SMatt Spinler try 40529d18c11SMatt Spinler { 40629d18c11SMatt Spinler updatePEL(attr->second.path, func); 40729d18c11SMatt Spinler 40829d18c11SMatt Spinler attr->second.hostState = state; 40929d18c11SMatt Spinler } 41066491c61SPatrick Williams catch (const std::exception& e) 41129d18c11SMatt Spinler { 41229d18c11SMatt Spinler log<level::ERR>("Unable to update PEL host transmission state", 41329d18c11SMatt Spinler entry("PATH=%s", attr->second.path.c_str()), 41429d18c11SMatt Spinler entry("ERROR=%s", e.what())); 41529d18c11SMatt Spinler } 41629d18c11SMatt Spinler } 41729d18c11SMatt Spinler } 41829d18c11SMatt Spinler 41929d18c11SMatt Spinler void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state) 42029d18c11SMatt Spinler { 42129d18c11SMatt Spinler LogID id{LogID::Pel{pelID}}; 42229d18c11SMatt Spinler auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 42329d18c11SMatt Spinler [&id](const auto& a) { return a.first == id; }); 42429d18c11SMatt Spinler 42529d18c11SMatt Spinler if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state)) 42629d18c11SMatt Spinler { 42729d18c11SMatt Spinler PELUpdateFunc func = [state](PEL& pel) { 42829d18c11SMatt Spinler pel.setHMCTransmissionState(state); 42929d18c11SMatt Spinler }; 43029d18c11SMatt Spinler 43129d18c11SMatt Spinler try 43229d18c11SMatt Spinler { 43329d18c11SMatt Spinler updatePEL(attr->second.path, func); 43429d18c11SMatt Spinler 43529d18c11SMatt Spinler attr->second.hmcState = state; 43629d18c11SMatt Spinler } 43766491c61SPatrick Williams catch (const std::exception& e) 43829d18c11SMatt Spinler { 43929d18c11SMatt Spinler log<level::ERR>("Unable to update PEL HMC transmission state", 44029d18c11SMatt Spinler entry("PATH=%s", attr->second.path.c_str()), 44129d18c11SMatt Spinler entry("ERROR=%s", e.what())); 44229d18c11SMatt Spinler } 44329d18c11SMatt Spinler } 44429d18c11SMatt Spinler } 44529d18c11SMatt Spinler 44629d18c11SMatt Spinler void Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc) 44729d18c11SMatt Spinler { 44829d18c11SMatt Spinler std::ifstream file{path}; 44929d18c11SMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 45029d18c11SMatt Spinler std::istreambuf_iterator<char>()}; 45129d18c11SMatt Spinler file.close(); 45229d18c11SMatt Spinler 45329d18c11SMatt Spinler PEL pel{data}; 45429d18c11SMatt Spinler 45529d18c11SMatt Spinler if (pel.valid()) 45629d18c11SMatt Spinler { 45729d18c11SMatt Spinler updateFunc(pel); 45829d18c11SMatt Spinler 45929d18c11SMatt Spinler write(pel, path); 46029d18c11SMatt Spinler } 46129d18c11SMatt Spinler else 46229d18c11SMatt Spinler { 46329d18c11SMatt Spinler throw std::runtime_error( 46429d18c11SMatt Spinler "Unable to read a valid PEL when trying to update it"); 46529d18c11SMatt Spinler } 46629d18c11SMatt Spinler } 46729d18c11SMatt Spinler 468b188f78aSMatt Spinler bool Repository::isServiceableSev(const PELAttributes& pel) 469b188f78aSMatt Spinler { 470b188f78aSMatt Spinler auto sevType = static_cast<SeverityType>(pel.severity & 0xF0); 471*2544b419SPatrick Williams auto sevPVEntry = pel_values::findByValue(pel.severity, 472*2544b419SPatrick Williams pel_values::severityValues); 473b188f78aSMatt Spinler std::string sevName = std::get<pel_values::registryNamePos>(*sevPVEntry); 474b188f78aSMatt Spinler 475b188f78aSMatt Spinler bool check1 = (sevType == SeverityType::predictive) || 476b188f78aSMatt Spinler (sevType == SeverityType::unrecoverable) || 477b188f78aSMatt Spinler (sevType == SeverityType::critical); 478b188f78aSMatt Spinler 479b188f78aSMatt Spinler bool check2 = ((sevType == SeverityType::recovered) || 480b188f78aSMatt Spinler (sevName == "symptom_recovered")) && 481b188f78aSMatt Spinler !pel.actionFlags.test(hiddenFlagBit); 482b188f78aSMatt Spinler 483b188f78aSMatt Spinler bool check3 = (sevName == "symptom_predictive") || 484b188f78aSMatt Spinler (sevName == "symptom_unrecoverable") || 485b188f78aSMatt Spinler (sevName == "symptom_critical"); 486b188f78aSMatt Spinler 487b188f78aSMatt Spinler return check1 || check2 || check3; 488b188f78aSMatt Spinler } 489b188f78aSMatt Spinler 490b188f78aSMatt Spinler void Repository::updateRepoStats(const PELAttributes& pel, bool pelAdded) 491b188f78aSMatt Spinler { 492b188f78aSMatt Spinler auto isServiceable = Repository::isServiceableSev(pel); 493b188f78aSMatt Spinler auto bmcPEL = CreatorID::openBMC == static_cast<CreatorID>(pel.creator); 494b188f78aSMatt Spinler 495b188f78aSMatt Spinler auto adjustSize = [pelAdded, &pel](auto& runningSize) { 496b188f78aSMatt Spinler if (pelAdded) 497b188f78aSMatt Spinler { 498b188f78aSMatt Spinler runningSize += pel.sizeOnDisk; 499b188f78aSMatt Spinler } 500b188f78aSMatt Spinler else 501b188f78aSMatt Spinler { 502b188f78aSMatt Spinler runningSize = std::max(static_cast<int64_t>(runningSize) - 503b188f78aSMatt Spinler static_cast<int64_t>(pel.sizeOnDisk), 504b188f78aSMatt Spinler static_cast<int64_t>(0)); 505b188f78aSMatt Spinler } 506b188f78aSMatt Spinler }; 507b188f78aSMatt Spinler 508b188f78aSMatt Spinler adjustSize(_sizes.total); 509b188f78aSMatt Spinler 510b188f78aSMatt Spinler if (bmcPEL) 511b188f78aSMatt Spinler { 512b188f78aSMatt Spinler adjustSize(_sizes.bmc); 513b188f78aSMatt Spinler if (isServiceable) 514b188f78aSMatt Spinler { 515b188f78aSMatt Spinler adjustSize(_sizes.bmcServiceable); 516b188f78aSMatt Spinler } 517b188f78aSMatt Spinler else 518b188f78aSMatt Spinler { 519b188f78aSMatt Spinler adjustSize(_sizes.bmcInfo); 520b188f78aSMatt Spinler } 521b188f78aSMatt Spinler } 522b188f78aSMatt Spinler else 523b188f78aSMatt Spinler { 524b188f78aSMatt Spinler adjustSize(_sizes.nonBMC); 525b188f78aSMatt Spinler if (isServiceable) 526b188f78aSMatt Spinler { 527b188f78aSMatt Spinler adjustSize(_sizes.nonBMCServiceable); 528b188f78aSMatt Spinler } 529b188f78aSMatt Spinler else 530b188f78aSMatt Spinler { 531b188f78aSMatt Spinler adjustSize(_sizes.nonBMCInfo); 532b188f78aSMatt Spinler } 533b188f78aSMatt Spinler } 534b188f78aSMatt Spinler } 535b188f78aSMatt Spinler 536c296692bSSumit Kumar bool Repository::sizeWarning() 5377e727a39SMatt Spinler { 538c296692bSSumit Kumar std::error_code ec; 539c296692bSSumit Kumar 5401d8835bbSSumit Kumar if ((_archiveSize > 0) && ((_sizes.total + _archiveSize) > 5411d8835bbSSumit Kumar ((_maxRepoSize * warningPercentage) / 100))) 5421d8835bbSSumit Kumar { 5431d8835bbSSumit Kumar log<level::INFO>( 5441d8835bbSSumit Kumar "Repository::sizeWarning function:Deleting the files in archive"); 5451d8835bbSSumit Kumar 546c296692bSSumit Kumar for (const auto& dirEntry : fs::directory_iterator(_archivePath)) 5471d8835bbSSumit Kumar { 548c296692bSSumit Kumar fs::remove(dirEntry.path(), ec); 549c296692bSSumit Kumar if (ec) 550c296692bSSumit Kumar { 551c296692bSSumit Kumar log<level::INFO>( 552c296692bSSumit Kumar "Repository::sizeWarning function:Could not delete " 553c296692bSSumit Kumar "a file in PEL archive", 554c296692bSSumit Kumar entry("FILENAME=%s", dirEntry.path().c_str())); 5551d8835bbSSumit Kumar } 5561d8835bbSSumit Kumar } 5571d8835bbSSumit Kumar 558c296692bSSumit Kumar _archiveSize = 0; 559c296692bSSumit Kumar } 560c296692bSSumit Kumar 5617e727a39SMatt Spinler return (_sizes.total > (_maxRepoSize * warningPercentage / 100)) || 5627e727a39SMatt Spinler (_pelAttributes.size() > _maxNumPELs); 5637e727a39SMatt Spinler } 5647e727a39SMatt Spinler 565b0a8df5bSMatt Spinler std::vector<Repository::AttributesReference> 566b0a8df5bSMatt Spinler Repository::getAllPELAttributes(SortOrder order) const 567b0a8df5bSMatt Spinler { 568b0a8df5bSMatt Spinler std::vector<Repository::AttributesReference> attributes; 569b0a8df5bSMatt Spinler 570b0a8df5bSMatt Spinler std::for_each( 571b0a8df5bSMatt Spinler _pelAttributes.begin(), _pelAttributes.end(), 572b0a8df5bSMatt Spinler [&attributes](auto& pelEntry) { attributes.push_back(pelEntry); }); 573b0a8df5bSMatt Spinler 574b0a8df5bSMatt Spinler std::sort(attributes.begin(), attributes.end(), 575b0a8df5bSMatt Spinler [order](const auto& left, const auto& right) { 576b0a8df5bSMatt Spinler if (order == SortOrder::ascending) 577b0a8df5bSMatt Spinler { 578b0a8df5bSMatt Spinler return left.get().second.path < right.get().second.path; 579b0a8df5bSMatt Spinler } 580b0a8df5bSMatt Spinler return left.get().second.path > right.get().second.path; 581b0a8df5bSMatt Spinler }); 582b0a8df5bSMatt Spinler 583b0a8df5bSMatt Spinler return attributes; 584b0a8df5bSMatt Spinler } 585b0a8df5bSMatt Spinler 586027bf285SSumit Kumar std::vector<uint32_t> 587027bf285SSumit Kumar Repository::prune(const std::vector<uint32_t>& idsWithHwIsoEntry) 588b0a8df5bSMatt Spinler { 589b0a8df5bSMatt Spinler std::vector<uint32_t> obmcLogIDs; 590b0a8df5bSMatt Spinler std::string msg = "Pruning PEL repository that takes up " + 591b0a8df5bSMatt Spinler std::to_string(_sizes.total) + " bytes and has " + 592b0a8df5bSMatt Spinler std::to_string(_pelAttributes.size()) + " PELs"; 593b0a8df5bSMatt Spinler log<level::INFO>(msg.c_str()); 594b0a8df5bSMatt Spinler 595b0a8df5bSMatt Spinler // Set up the 5 functions to check if the PEL category 596b0a8df5bSMatt Spinler // is still over its limits. 597b0a8df5bSMatt Spinler 598b0a8df5bSMatt Spinler // BMC informational PELs should only take up 15% 599b0a8df5bSMatt Spinler IsOverLimitFunc overBMCInfoLimit = [this]() { 600b0a8df5bSMatt Spinler return _sizes.bmcInfo > _maxRepoSize * 15 / 100; 601b0a8df5bSMatt Spinler }; 602b0a8df5bSMatt Spinler 603b0a8df5bSMatt Spinler // BMC non informational PELs should only take up 30% 604b0a8df5bSMatt Spinler IsOverLimitFunc overBMCNonInfoLimit = [this]() { 605b0a8df5bSMatt Spinler return _sizes.bmcServiceable > _maxRepoSize * 30 / 100; 606b0a8df5bSMatt Spinler }; 607b0a8df5bSMatt Spinler 608b0a8df5bSMatt Spinler // Non BMC informational PELs should only take up 15% 609b0a8df5bSMatt Spinler IsOverLimitFunc overNonBMCInfoLimit = [this]() { 610b0a8df5bSMatt Spinler return _sizes.nonBMCInfo > _maxRepoSize * 15 / 100; 611b0a8df5bSMatt Spinler }; 612b0a8df5bSMatt Spinler 613b0a8df5bSMatt Spinler // Non BMC non informational PELs should only take up 15% 614b0a8df5bSMatt Spinler IsOverLimitFunc overNonBMCNonInfoLimit = [this]() { 615b0a8df5bSMatt Spinler return _sizes.nonBMCServiceable > _maxRepoSize * 30 / 100; 616b0a8df5bSMatt Spinler }; 617b0a8df5bSMatt Spinler 618b0a8df5bSMatt Spinler // Bring the total number of PELs down to 80% of the max 619b0a8df5bSMatt Spinler IsOverLimitFunc tooManyPELsLimit = [this]() { 620b0a8df5bSMatt Spinler return _pelAttributes.size() > _maxNumPELs * 80 / 100; 621b0a8df5bSMatt Spinler }; 622b0a8df5bSMatt Spinler 623b0a8df5bSMatt Spinler // Set up the functions to determine which category a PEL is in. 624b0a8df5bSMatt Spinler // TODO: Return false in these functions if a PEL caused a guard record. 625b0a8df5bSMatt Spinler 626b0a8df5bSMatt Spinler // A BMC informational PEL 627b0a8df5bSMatt Spinler IsPELTypeFunc isBMCInfo = [](const PELAttributes& pel) { 628b0a8df5bSMatt Spinler return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) && 629b0a8df5bSMatt Spinler !Repository::isServiceableSev(pel); 630b0a8df5bSMatt Spinler }; 631b0a8df5bSMatt Spinler 632b0a8df5bSMatt Spinler // A BMC non informational PEL 633b0a8df5bSMatt Spinler IsPELTypeFunc isBMCNonInfo = [](const PELAttributes& pel) { 634b0a8df5bSMatt Spinler return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) && 635b0a8df5bSMatt Spinler Repository::isServiceableSev(pel); 636b0a8df5bSMatt Spinler }; 637b0a8df5bSMatt Spinler 638b0a8df5bSMatt Spinler // A non BMC informational PEL 639b0a8df5bSMatt Spinler IsPELTypeFunc isNonBMCInfo = [](const PELAttributes& pel) { 640b0a8df5bSMatt Spinler return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) && 641b0a8df5bSMatt Spinler !Repository::isServiceableSev(pel); 642b0a8df5bSMatt Spinler }; 643b0a8df5bSMatt Spinler 644b0a8df5bSMatt Spinler // A non BMC non informational PEL 645b0a8df5bSMatt Spinler IsPELTypeFunc isNonBMCNonInfo = [](const PELAttributes& pel) { 646b0a8df5bSMatt Spinler return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) && 647b0a8df5bSMatt Spinler Repository::isServiceableSev(pel); 648b0a8df5bSMatt Spinler }; 649b0a8df5bSMatt Spinler 650b0a8df5bSMatt Spinler // When counting PELs, count every PEL 651d26fa3e7SPatrick Williams IsPELTypeFunc isAnyPEL = [](const PELAttributes& /*pel*/) { return true; }; 652b0a8df5bSMatt Spinler 653b0a8df5bSMatt Spinler // Check all 4 categories, which will result in at most 90% 654b0a8df5bSMatt Spinler // usage (15 + 30 + 15 + 30). 655027bf285SSumit Kumar removePELs(overBMCInfoLimit, isBMCInfo, idsWithHwIsoEntry, obmcLogIDs); 656027bf285SSumit Kumar removePELs(overBMCNonInfoLimit, isBMCNonInfo, idsWithHwIsoEntry, 657027bf285SSumit Kumar obmcLogIDs); 658027bf285SSumit Kumar removePELs(overNonBMCInfoLimit, isNonBMCInfo, idsWithHwIsoEntry, 659027bf285SSumit Kumar obmcLogIDs); 660027bf285SSumit Kumar removePELs(overNonBMCNonInfoLimit, isNonBMCNonInfo, idsWithHwIsoEntry, 661027bf285SSumit Kumar obmcLogIDs); 662b0a8df5bSMatt Spinler 663b0a8df5bSMatt Spinler // After the above pruning check if there are still too many PELs, 664b0a8df5bSMatt Spinler // which can happen depending on PEL sizes. 665b0a8df5bSMatt Spinler if (_pelAttributes.size() > _maxNumPELs) 666b0a8df5bSMatt Spinler { 667027bf285SSumit Kumar removePELs(tooManyPELsLimit, isAnyPEL, idsWithHwIsoEntry, obmcLogIDs); 668b0a8df5bSMatt Spinler } 669b0a8df5bSMatt Spinler 670b0a8df5bSMatt Spinler if (!obmcLogIDs.empty()) 671b0a8df5bSMatt Spinler { 67245796e82SMatt Spinler std::string m = "Number of PELs removed to save space: " + 673b0a8df5bSMatt Spinler std::to_string(obmcLogIDs.size()); 67445796e82SMatt Spinler log<level::INFO>(m.c_str()); 675b0a8df5bSMatt Spinler } 676b0a8df5bSMatt Spinler 677b0a8df5bSMatt Spinler return obmcLogIDs; 678b0a8df5bSMatt Spinler } 679b0a8df5bSMatt Spinler 68045796e82SMatt Spinler void Repository::removePELs(const IsOverLimitFunc& isOverLimit, 68145796e82SMatt Spinler const IsPELTypeFunc& isPELType, 682027bf285SSumit Kumar const std::vector<uint32_t>& idsWithHwIsoEntry, 683b0a8df5bSMatt Spinler std::vector<uint32_t>& removedBMCLogIDs) 684b0a8df5bSMatt Spinler { 685b0a8df5bSMatt Spinler if (!isOverLimit()) 686b0a8df5bSMatt Spinler { 687b0a8df5bSMatt Spinler return; 688b0a8df5bSMatt Spinler } 689b0a8df5bSMatt Spinler 690b0a8df5bSMatt Spinler auto attributes = getAllPELAttributes(SortOrder::ascending); 691b0a8df5bSMatt Spinler 692b0a8df5bSMatt Spinler // Make 4 passes on the PELs, stopping as soon as isOverLimit 693b0a8df5bSMatt Spinler // returns false. 694b0a8df5bSMatt Spinler // Pass 1: only delete HMC acked PELs 695b0a8df5bSMatt Spinler // Pass 2: only delete OS acked PELs 696b0a8df5bSMatt Spinler // Pass 3: only delete PHYP sent PELs 697b0a8df5bSMatt Spinler // Pass 4: delete all PELs 698b0a8df5bSMatt Spinler static const std::vector<std::function<bool(const PELAttributes& pel)>> 699b0a8df5bSMatt Spinler stateChecks{[](const auto& pel) { 700b0a8df5bSMatt Spinler return pel.hmcState == TransmissionState::acked; 701b0a8df5bSMatt Spinler }, 702b0a8df5bSMatt Spinler 703b0a8df5bSMatt Spinler [](const auto& pel) { 704b0a8df5bSMatt Spinler return pel.hostState == TransmissionState::acked; 705b0a8df5bSMatt Spinler }, 706b0a8df5bSMatt Spinler 707b0a8df5bSMatt Spinler [](const auto& pel) { 708b0a8df5bSMatt Spinler return pel.hostState == TransmissionState::sent; 709b0a8df5bSMatt Spinler }, 710b0a8df5bSMatt Spinler 711d26fa3e7SPatrick Williams [](const auto& /*pel*/) { return true; }}; 712b0a8df5bSMatt Spinler 713b0a8df5bSMatt Spinler for (const auto& stateCheck : stateChecks) 714b0a8df5bSMatt Spinler { 715b0a8df5bSMatt Spinler for (auto it = attributes.begin(); it != attributes.end();) 716b0a8df5bSMatt Spinler { 717b0a8df5bSMatt Spinler const auto& pel = it->get(); 718b0a8df5bSMatt Spinler if (isPELType(pel.second) && stateCheck(pel.second)) 719b0a8df5bSMatt Spinler { 720b0a8df5bSMatt Spinler auto removedID = pel.first.obmcID.id; 721027bf285SSumit Kumar 722027bf285SSumit Kumar auto idFound = std::find(idsWithHwIsoEntry.begin(), 723027bf285SSumit Kumar idsWithHwIsoEntry.end(), removedID); 724027bf285SSumit Kumar if (idFound != idsWithHwIsoEntry.end()) 725027bf285SSumit Kumar { 726027bf285SSumit Kumar ++it; 727027bf285SSumit Kumar continue; 728027bf285SSumit Kumar } 729027bf285SSumit Kumar 730b0a8df5bSMatt Spinler remove(pel.first); 731b0a8df5bSMatt Spinler 732b0a8df5bSMatt Spinler removedBMCLogIDs.push_back(removedID); 733b0a8df5bSMatt Spinler 734b0a8df5bSMatt Spinler attributes.erase(it); 735b0a8df5bSMatt Spinler 736b0a8df5bSMatt Spinler if (!isOverLimit()) 737b0a8df5bSMatt Spinler { 738b0a8df5bSMatt Spinler break; 739b0a8df5bSMatt Spinler } 740b0a8df5bSMatt Spinler } 741b0a8df5bSMatt Spinler else 742b0a8df5bSMatt Spinler { 743b0a8df5bSMatt Spinler ++it; 744b0a8df5bSMatt Spinler } 745b0a8df5bSMatt Spinler } 746b0a8df5bSMatt Spinler 747b0a8df5bSMatt Spinler if (!isOverLimit()) 748b0a8df5bSMatt Spinler { 749b0a8df5bSMatt Spinler break; 750b0a8df5bSMatt Spinler } 751b0a8df5bSMatt Spinler } 752b0a8df5bSMatt Spinler } 753b0a8df5bSMatt Spinler 7542ccdcef9SSumit Kumar void Repository::archivePEL(const PEL& pel) 7552ccdcef9SSumit Kumar { 7562ccdcef9SSumit Kumar if (pel.valid()) 7572ccdcef9SSumit Kumar { 7582ccdcef9SSumit Kumar auto path = _archivePath / getPELFilename(pel.id(), pel.commitTime()); 7592ccdcef9SSumit Kumar 7602ccdcef9SSumit Kumar write(pel, path); 7612ccdcef9SSumit Kumar 7622ccdcef9SSumit Kumar _archiveSize += getFileDiskSize(path); 7632ccdcef9SSumit Kumar } 7642ccdcef9SSumit Kumar } 7652ccdcef9SSumit Kumar 76689fa082aSMatt Spinler } // namespace pels 76789fa082aSMatt Spinler } // namespace openpower 768