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 18*32a6df6cSMatt 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 118dd325c32SMatt Spinler PELAttributes attributes{dirEntry.path(), 119dd325c32SMatt Spinler getFileDiskSize(dirEntry.path()), 120dd325c32SMatt Spinler pel.privateHeader().creatorID(), 121afb1b46fSVijay Lobo pel.userHeader().subsystem(), 122dd325c32SMatt Spinler pel.userHeader().severity(), 123dd325c32SMatt Spinler pel.userHeader().actionFlags(), 124dd325c32SMatt Spinler pel.hostTransmissionState(), 125dd325c32SMatt Spinler pel.hmcTransmissionState()}; 1260ff00485SMatt Spinler 127475e574dSMatt Spinler using pelID = LogID::Pel; 128475e574dSMatt Spinler using obmcID = LogID::Obmc; 1290ff00485SMatt Spinler _pelAttributes.emplace( 130475e574dSMatt Spinler LogID(pelID(pel.id()), obmcID(pel.obmcLogID())), 1310ff00485SMatt Spinler attributes); 132b188f78aSMatt Spinler 133b188f78aSMatt Spinler updateRepoStats(attributes, true); 134475e574dSMatt Spinler } 135475e574dSMatt Spinler else 136475e574dSMatt Spinler { 137475e574dSMatt Spinler log<level::ERR>( 138475e574dSMatt Spinler "Found invalid PEL file while restoring. Removing.", 139475e574dSMatt Spinler entry("FILENAME=%s", dirEntry.path().c_str())); 140475e574dSMatt Spinler fs::remove(dirEntry.path()); 141475e574dSMatt Spinler } 142475e574dSMatt Spinler } 14366491c61SPatrick Williams catch (const std::exception& e) 144475e574dSMatt Spinler { 145475e574dSMatt Spinler log<level::ERR>("Hit exception while restoring PEL File", 146475e574dSMatt Spinler entry("FILENAME=%s", dirEntry.path().c_str()), 147475e574dSMatt Spinler entry("ERROR=%s", e.what())); 148475e574dSMatt Spinler } 149475e574dSMatt Spinler } 1501d8835bbSSumit Kumar 1511d8835bbSSumit Kumar // Get size of archive folder 1521d8835bbSSumit Kumar for (auto& dirEntry : fs::directory_iterator(_archivePath)) 1531d8835bbSSumit Kumar { 1541d8835bbSSumit Kumar _archiveSize += getFileDiskSize(dirEntry); 1551d8835bbSSumit Kumar } 15689fa082aSMatt Spinler } 15789fa082aSMatt Spinler 15889fa082aSMatt Spinler std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time) 15989fa082aSMatt Spinler { 16089fa082aSMatt Spinler char name[50]; 16189fa082aSMatt Spinler sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB, 16289fa082aSMatt Spinler time.yearLSB, time.month, time.day, time.hour, time.minutes, 16389fa082aSMatt Spinler time.seconds, time.hundredths, pelID); 16489fa082aSMatt Spinler return std::string{name}; 16589fa082aSMatt Spinler } 16689fa082aSMatt Spinler 16789fa082aSMatt Spinler void Repository::add(std::unique_ptr<PEL>& pel) 16889fa082aSMatt Spinler { 169df43a305SMatt Spinler pel->setHostTransmissionState(TransmissionState::newPEL); 170df43a305SMatt Spinler pel->setHMCTransmissionState(TransmissionState::newPEL); 171df43a305SMatt Spinler 17289fa082aSMatt Spinler auto path = _logPath / getPELFilename(pel->id(), pel->commitTime()); 173ab1b97feSMatt Spinler 174ab1b97feSMatt Spinler write(*(pel.get()), path); 175ab1b97feSMatt Spinler 176dd325c32SMatt Spinler PELAttributes attributes{path, 177dd325c32SMatt Spinler getFileDiskSize(path), 178dd325c32SMatt Spinler pel->privateHeader().creatorID(), 179afb1b46fSVijay Lobo pel->userHeader().subsystem(), 180dd325c32SMatt Spinler pel->userHeader().severity(), 181dd325c32SMatt Spinler pel->userHeader().actionFlags(), 182346f99a1SMatt Spinler pel->hostTransmissionState(), 183346f99a1SMatt Spinler pel->hmcTransmissionState()}; 184ab1b97feSMatt Spinler 185ab1b97feSMatt Spinler using pelID = LogID::Pel; 186ab1b97feSMatt Spinler using obmcID = LogID::Obmc; 187ab1b97feSMatt Spinler _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())), 188ab1b97feSMatt Spinler attributes); 189ab1b97feSMatt Spinler 19044893cc9SMatt Spinler _lastPelID = pel->id(); 19144893cc9SMatt Spinler 192b188f78aSMatt Spinler updateRepoStats(attributes, true); 193b188f78aSMatt Spinler 194ab1b97feSMatt Spinler processAddCallbacks(*pel); 195ab1b97feSMatt Spinler } 196ab1b97feSMatt Spinler 197ab1b97feSMatt Spinler void Repository::write(const PEL& pel, const fs::path& path) 198ab1b97feSMatt Spinler { 19989fa082aSMatt Spinler std::ofstream file{path, std::ios::binary}; 20089fa082aSMatt Spinler 20189fa082aSMatt Spinler if (!file.good()) 20289fa082aSMatt Spinler { 20389fa082aSMatt Spinler // If this fails, the filesystem is probably full so it isn't like 20489fa082aSMatt Spinler // we could successfully create yet another error log here. 20589fa082aSMatt Spinler auto e = errno; 20689fa082aSMatt Spinler fs::remove(path); 20789fa082aSMatt Spinler log<level::ERR>("Unable to open PEL file for writing", 20889fa082aSMatt Spinler entry("ERRNO=%d", e), entry("PATH=%s", path.c_str())); 20989fa082aSMatt Spinler throw file_error::Open(); 21089fa082aSMatt Spinler } 21189fa082aSMatt Spinler 212ab1b97feSMatt Spinler auto data = pel.data(); 21389fa082aSMatt Spinler file.write(reinterpret_cast<const char*>(data.data()), data.size()); 21489fa082aSMatt Spinler 21589fa082aSMatt Spinler if (file.fail()) 21689fa082aSMatt Spinler { 21789fa082aSMatt Spinler // Same note as above about not being able to create an error log 21889fa082aSMatt Spinler // for this case even if we wanted. 21989fa082aSMatt Spinler auto e = errno; 22089fa082aSMatt Spinler file.close(); 22189fa082aSMatt Spinler fs::remove(path); 22289fa082aSMatt Spinler log<level::ERR>("Unable to write PEL file", entry("ERRNO=%d", e), 22389fa082aSMatt Spinler entry("PATH=%s", path.c_str())); 22489fa082aSMatt Spinler throw file_error::Write(); 22589fa082aSMatt Spinler } 226475e574dSMatt Spinler } 227475e574dSMatt Spinler 22852602e35SMatt Spinler std::optional<Repository::LogID> Repository::remove(const LogID& id) 229475e574dSMatt Spinler { 230475e574dSMatt Spinler auto pel = findPEL(id); 231ff6b598bSPatrick Williams if (pel == _pelAttributes.end()) 232475e574dSMatt Spinler { 233ff6b598bSPatrick Williams return std::nullopt; 234ff6b598bSPatrick Williams } 235ff6b598bSPatrick Williams 236ff6b598bSPatrick Williams LogID actualID = pel->first; 237b188f78aSMatt Spinler updateRepoStats(pel->second, false); 238b188f78aSMatt Spinler 2395f5352e5SMatt Spinler log<level::DEBUG>("Removing PEL from repository", 240ff6b598bSPatrick Williams entry("PEL_ID=0x%X", actualID.pelID.id), 241ff6b598bSPatrick Williams entry("OBMC_LOG_ID=%d", actualID.obmcID.id)); 2421d8835bbSSumit Kumar 2431d8835bbSSumit Kumar if (fs::exists(pel->second.path)) 2441d8835bbSSumit Kumar { 2451d8835bbSSumit Kumar // Check for existense of new archive folder 2461d8835bbSSumit Kumar if (!fs::exists(_archivePath)) 2471d8835bbSSumit Kumar { 2481d8835bbSSumit Kumar fs::create_directories(_archivePath); 2491d8835bbSSumit Kumar } 2501d8835bbSSumit Kumar 2511d8835bbSSumit Kumar // Move log file to archive folder 2521d8835bbSSumit Kumar auto fileName = _archivePath / pel->second.path.filename(); 2531d8835bbSSumit Kumar fs::rename(pel->second.path, fileName); 2541d8835bbSSumit Kumar 2551d8835bbSSumit Kumar // Update size of file 2561d8835bbSSumit Kumar _archiveSize += getFileDiskSize(fileName); 2571d8835bbSSumit Kumar } 2581d8835bbSSumit Kumar 2590ff00485SMatt Spinler _pelAttributes.erase(pel); 260421f6531SMatt Spinler 261ff6b598bSPatrick Williams processDeleteCallbacks(actualID.pelID.id); 26252602e35SMatt Spinler 26352602e35SMatt Spinler return actualID; 26489fa082aSMatt Spinler } 26589fa082aSMatt Spinler 2662813f36dSMatt Spinler std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id) 2672813f36dSMatt Spinler { 2682813f36dSMatt Spinler auto pel = findPEL(id); 2690ff00485SMatt Spinler if (pel != _pelAttributes.end()) 2702813f36dSMatt Spinler { 2710ff00485SMatt Spinler std::ifstream file{pel->second.path.c_str()}; 2722813f36dSMatt Spinler if (!file.good()) 2732813f36dSMatt Spinler { 2742813f36dSMatt Spinler auto e = errno; 2752813f36dSMatt Spinler log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e), 2760ff00485SMatt Spinler entry("PATH=%s", pel->second.path.c_str())); 2772813f36dSMatt Spinler throw file_error::Open(); 2782813f36dSMatt Spinler } 2792813f36dSMatt Spinler 2802813f36dSMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 2812813f36dSMatt Spinler std::istreambuf_iterator<char>()}; 2822813f36dSMatt Spinler return data; 2832813f36dSMatt Spinler } 2842813f36dSMatt Spinler 2852813f36dSMatt Spinler return std::nullopt; 2862813f36dSMatt Spinler } 2872813f36dSMatt Spinler 2886d51224bSMatt Spinler std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id) 2896d51224bSMatt Spinler { 2906d51224bSMatt Spinler auto pel = findPEL(id); 2916d51224bSMatt Spinler if (pel != _pelAttributes.end()) 2926d51224bSMatt Spinler { 293*32a6df6cSMatt Spinler int fd = open(pel->second.path.c_str(), O_RDONLY | O_NONBLOCK); 294*32a6df6cSMatt Spinler if (fd == -1) 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. 304*32a6df6cSMatt Spinler return fd; 3056d51224bSMatt Spinler } 3066d51224bSMatt Spinler return std::nullopt; 3076d51224bSMatt Spinler } 3086d51224bSMatt Spinler 3091ea78801SMatt Spinler void Repository::for_each(ForEachFunc func) const 3101ea78801SMatt Spinler { 3110ff00485SMatt Spinler for (const auto& [id, attributes] : _pelAttributes) 3121ea78801SMatt Spinler { 3130ff00485SMatt Spinler std::ifstream file{attributes.path}; 3141ea78801SMatt Spinler 3151ea78801SMatt Spinler if (!file.good()) 3161ea78801SMatt Spinler { 3171ea78801SMatt Spinler auto e = errno; 3181ea78801SMatt Spinler log<level::ERR>("Repository::for_each: Unable to open PEL file", 3191ea78801SMatt Spinler entry("ERRNO=%d", e), 3200ff00485SMatt Spinler entry("PATH=%s", attributes.path.c_str())); 3211ea78801SMatt Spinler continue; 3221ea78801SMatt Spinler } 3231ea78801SMatt Spinler 3241ea78801SMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 3251ea78801SMatt Spinler std::istreambuf_iterator<char>()}; 3261ea78801SMatt Spinler file.close(); 3271ea78801SMatt Spinler 3281ea78801SMatt Spinler PEL pel{data}; 3291ea78801SMatt Spinler 3301ea78801SMatt Spinler try 3311ea78801SMatt Spinler { 3321ea78801SMatt Spinler if (func(pel)) 3331ea78801SMatt Spinler { 3341ea78801SMatt Spinler break; 3351ea78801SMatt Spinler } 3361ea78801SMatt Spinler } 33766491c61SPatrick Williams catch (const std::exception& e) 3381ea78801SMatt Spinler { 3391ea78801SMatt Spinler log<level::ERR>("Repository::for_each function exception", 3401ea78801SMatt Spinler entry("ERROR=%s", e.what())); 3411ea78801SMatt Spinler } 3421ea78801SMatt Spinler } 3431ea78801SMatt Spinler } 3441ea78801SMatt Spinler 345421f6531SMatt Spinler void Repository::processAddCallbacks(const PEL& pel) const 346421f6531SMatt Spinler { 347421f6531SMatt Spinler for (auto& [name, func] : _addSubscriptions) 348421f6531SMatt Spinler { 349421f6531SMatt Spinler try 350421f6531SMatt Spinler { 351421f6531SMatt Spinler func(pel); 352421f6531SMatt Spinler } 35366491c61SPatrick Williams catch (const std::exception& e) 354421f6531SMatt Spinler { 355421f6531SMatt Spinler log<level::ERR>("PEL Repository add callback exception", 356421f6531SMatt Spinler entry("NAME=%s", name.c_str()), 357421f6531SMatt Spinler entry("ERROR=%s", e.what())); 358421f6531SMatt Spinler } 359421f6531SMatt Spinler } 360421f6531SMatt Spinler } 361421f6531SMatt Spinler 362421f6531SMatt Spinler void Repository::processDeleteCallbacks(uint32_t id) const 363421f6531SMatt Spinler { 364421f6531SMatt Spinler for (auto& [name, func] : _deleteSubscriptions) 365421f6531SMatt Spinler { 366421f6531SMatt Spinler try 367421f6531SMatt Spinler { 368421f6531SMatt Spinler func(id); 369421f6531SMatt Spinler } 37066491c61SPatrick Williams catch (const std::exception& e) 371421f6531SMatt Spinler { 372421f6531SMatt Spinler log<level::ERR>("PEL Repository delete callback exception", 373421f6531SMatt Spinler entry("NAME=%s", name.c_str()), 374421f6531SMatt Spinler entry("ERROR=%s", e.what())); 375421f6531SMatt Spinler } 376421f6531SMatt Spinler } 377421f6531SMatt Spinler } 378421f6531SMatt Spinler 3790ff00485SMatt Spinler std::optional<std::reference_wrapper<const Repository::PELAttributes>> 3800ff00485SMatt Spinler Repository::getPELAttributes(const LogID& id) const 3810ff00485SMatt Spinler { 3820ff00485SMatt Spinler auto pel = findPEL(id); 3830ff00485SMatt Spinler if (pel != _pelAttributes.end()) 3840ff00485SMatt Spinler { 3850ff00485SMatt Spinler return pel->second; 3860ff00485SMatt Spinler } 3870ff00485SMatt Spinler 3880ff00485SMatt Spinler return std::nullopt; 3890ff00485SMatt Spinler } 3900ff00485SMatt Spinler 39129d18c11SMatt Spinler void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state) 39229d18c11SMatt Spinler { 39329d18c11SMatt Spinler LogID id{LogID::Pel{pelID}}; 39429d18c11SMatt Spinler auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 39529d18c11SMatt Spinler [&id](const auto& a) { return a.first == id; }); 39629d18c11SMatt Spinler 39729d18c11SMatt Spinler if ((attr != _pelAttributes.end()) && (attr->second.hostState != state)) 39829d18c11SMatt Spinler { 39929d18c11SMatt Spinler PELUpdateFunc func = [state](PEL& pel) { 40029d18c11SMatt Spinler pel.setHostTransmissionState(state); 40129d18c11SMatt Spinler }; 40229d18c11SMatt Spinler 40329d18c11SMatt Spinler try 40429d18c11SMatt Spinler { 40529d18c11SMatt Spinler updatePEL(attr->second.path, func); 40629d18c11SMatt Spinler 40729d18c11SMatt Spinler attr->second.hostState = state; 40829d18c11SMatt Spinler } 40966491c61SPatrick Williams catch (const std::exception& e) 41029d18c11SMatt Spinler { 41129d18c11SMatt Spinler log<level::ERR>("Unable to update PEL host transmission state", 41229d18c11SMatt Spinler entry("PATH=%s", attr->second.path.c_str()), 41329d18c11SMatt Spinler entry("ERROR=%s", e.what())); 41429d18c11SMatt Spinler } 41529d18c11SMatt Spinler } 41629d18c11SMatt Spinler } 41729d18c11SMatt Spinler 41829d18c11SMatt Spinler void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state) 41929d18c11SMatt Spinler { 42029d18c11SMatt Spinler LogID id{LogID::Pel{pelID}}; 42129d18c11SMatt Spinler auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 42229d18c11SMatt Spinler [&id](const auto& a) { return a.first == id; }); 42329d18c11SMatt Spinler 42429d18c11SMatt Spinler if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state)) 42529d18c11SMatt Spinler { 42629d18c11SMatt Spinler PELUpdateFunc func = [state](PEL& pel) { 42729d18c11SMatt Spinler pel.setHMCTransmissionState(state); 42829d18c11SMatt Spinler }; 42929d18c11SMatt Spinler 43029d18c11SMatt Spinler try 43129d18c11SMatt Spinler { 43229d18c11SMatt Spinler updatePEL(attr->second.path, func); 43329d18c11SMatt Spinler 43429d18c11SMatt Spinler attr->second.hmcState = state; 43529d18c11SMatt Spinler } 43666491c61SPatrick Williams catch (const std::exception& e) 43729d18c11SMatt Spinler { 43829d18c11SMatt Spinler log<level::ERR>("Unable to update PEL HMC transmission state", 43929d18c11SMatt Spinler entry("PATH=%s", attr->second.path.c_str()), 44029d18c11SMatt Spinler entry("ERROR=%s", e.what())); 44129d18c11SMatt Spinler } 44229d18c11SMatt Spinler } 44329d18c11SMatt Spinler } 44429d18c11SMatt Spinler 44529d18c11SMatt Spinler void Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc) 44629d18c11SMatt Spinler { 44729d18c11SMatt Spinler std::ifstream file{path}; 44829d18c11SMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 44929d18c11SMatt Spinler std::istreambuf_iterator<char>()}; 45029d18c11SMatt Spinler file.close(); 45129d18c11SMatt Spinler 45229d18c11SMatt Spinler PEL pel{data}; 45329d18c11SMatt Spinler 45429d18c11SMatt Spinler if (pel.valid()) 45529d18c11SMatt Spinler { 45629d18c11SMatt Spinler updateFunc(pel); 45729d18c11SMatt Spinler 45829d18c11SMatt Spinler write(pel, path); 45929d18c11SMatt Spinler } 46029d18c11SMatt Spinler else 46129d18c11SMatt Spinler { 46229d18c11SMatt Spinler throw std::runtime_error( 46329d18c11SMatt Spinler "Unable to read a valid PEL when trying to update it"); 46429d18c11SMatt Spinler } 46529d18c11SMatt Spinler } 46629d18c11SMatt Spinler 467b188f78aSMatt Spinler bool Repository::isServiceableSev(const PELAttributes& pel) 468b188f78aSMatt Spinler { 469b188f78aSMatt Spinler auto sevType = static_cast<SeverityType>(pel.severity & 0xF0); 4702544b419SPatrick Williams auto sevPVEntry = pel_values::findByValue(pel.severity, 4712544b419SPatrick Williams pel_values::severityValues); 472b188f78aSMatt Spinler std::string sevName = std::get<pel_values::registryNamePos>(*sevPVEntry); 473b188f78aSMatt Spinler 474b188f78aSMatt Spinler bool check1 = (sevType == SeverityType::predictive) || 475b188f78aSMatt Spinler (sevType == SeverityType::unrecoverable) || 476b188f78aSMatt Spinler (sevType == SeverityType::critical); 477b188f78aSMatt Spinler 478b188f78aSMatt Spinler bool check2 = ((sevType == SeverityType::recovered) || 479b188f78aSMatt Spinler (sevName == "symptom_recovered")) && 480b188f78aSMatt Spinler !pel.actionFlags.test(hiddenFlagBit); 481b188f78aSMatt Spinler 482b188f78aSMatt Spinler bool check3 = (sevName == "symptom_predictive") || 483b188f78aSMatt Spinler (sevName == "symptom_unrecoverable") || 484b188f78aSMatt Spinler (sevName == "symptom_critical"); 485b188f78aSMatt Spinler 486b188f78aSMatt Spinler return check1 || check2 || check3; 487b188f78aSMatt Spinler } 488b188f78aSMatt Spinler 489b188f78aSMatt Spinler void Repository::updateRepoStats(const PELAttributes& pel, bool pelAdded) 490b188f78aSMatt Spinler { 491b188f78aSMatt Spinler auto isServiceable = Repository::isServiceableSev(pel); 492b188f78aSMatt Spinler auto bmcPEL = CreatorID::openBMC == static_cast<CreatorID>(pel.creator); 493b188f78aSMatt Spinler 494b188f78aSMatt Spinler auto adjustSize = [pelAdded, &pel](auto& runningSize) { 495b188f78aSMatt Spinler if (pelAdded) 496b188f78aSMatt Spinler { 497b188f78aSMatt Spinler runningSize += pel.sizeOnDisk; 498b188f78aSMatt Spinler } 499b188f78aSMatt Spinler else 500b188f78aSMatt Spinler { 501b188f78aSMatt Spinler runningSize = std::max(static_cast<int64_t>(runningSize) - 502b188f78aSMatt Spinler static_cast<int64_t>(pel.sizeOnDisk), 503b188f78aSMatt Spinler static_cast<int64_t>(0)); 504b188f78aSMatt Spinler } 505b188f78aSMatt Spinler }; 506b188f78aSMatt Spinler 507b188f78aSMatt Spinler adjustSize(_sizes.total); 508b188f78aSMatt Spinler 509b188f78aSMatt Spinler if (bmcPEL) 510b188f78aSMatt Spinler { 511b188f78aSMatt Spinler adjustSize(_sizes.bmc); 512b188f78aSMatt Spinler if (isServiceable) 513b188f78aSMatt Spinler { 514b188f78aSMatt Spinler adjustSize(_sizes.bmcServiceable); 515b188f78aSMatt Spinler } 516b188f78aSMatt Spinler else 517b188f78aSMatt Spinler { 518b188f78aSMatt Spinler adjustSize(_sizes.bmcInfo); 519b188f78aSMatt Spinler } 520b188f78aSMatt Spinler } 521b188f78aSMatt Spinler else 522b188f78aSMatt Spinler { 523b188f78aSMatt Spinler adjustSize(_sizes.nonBMC); 524b188f78aSMatt Spinler if (isServiceable) 525b188f78aSMatt Spinler { 526b188f78aSMatt Spinler adjustSize(_sizes.nonBMCServiceable); 527b188f78aSMatt Spinler } 528b188f78aSMatt Spinler else 529b188f78aSMatt Spinler { 530b188f78aSMatt Spinler adjustSize(_sizes.nonBMCInfo); 531b188f78aSMatt Spinler } 532b188f78aSMatt Spinler } 533b188f78aSMatt Spinler } 534b188f78aSMatt Spinler 535c296692bSSumit Kumar bool Repository::sizeWarning() 5367e727a39SMatt Spinler { 537c296692bSSumit Kumar std::error_code ec; 538c296692bSSumit Kumar 5391d8835bbSSumit Kumar if ((_archiveSize > 0) && ((_sizes.total + _archiveSize) > 5401d8835bbSSumit Kumar ((_maxRepoSize * warningPercentage) / 100))) 5411d8835bbSSumit Kumar { 5421d8835bbSSumit Kumar log<level::INFO>( 5431d8835bbSSumit Kumar "Repository::sizeWarning function:Deleting the files in archive"); 5441d8835bbSSumit Kumar 545c296692bSSumit Kumar for (const auto& dirEntry : fs::directory_iterator(_archivePath)) 5461d8835bbSSumit Kumar { 547c296692bSSumit Kumar fs::remove(dirEntry.path(), ec); 548c296692bSSumit Kumar if (ec) 549c296692bSSumit Kumar { 550c296692bSSumit Kumar log<level::INFO>( 551c296692bSSumit Kumar "Repository::sizeWarning function:Could not delete " 552c296692bSSumit Kumar "a file in PEL archive", 553c296692bSSumit Kumar entry("FILENAME=%s", dirEntry.path().c_str())); 5541d8835bbSSumit Kumar } 5551d8835bbSSumit Kumar } 5561d8835bbSSumit Kumar 557c296692bSSumit Kumar _archiveSize = 0; 558c296692bSSumit Kumar } 559c296692bSSumit Kumar 5607e727a39SMatt Spinler return (_sizes.total > (_maxRepoSize * warningPercentage / 100)) || 5617e727a39SMatt Spinler (_pelAttributes.size() > _maxNumPELs); 5627e727a39SMatt Spinler } 5637e727a39SMatt Spinler 564b0a8df5bSMatt Spinler std::vector<Repository::AttributesReference> 565b0a8df5bSMatt Spinler Repository::getAllPELAttributes(SortOrder order) const 566b0a8df5bSMatt Spinler { 567b0a8df5bSMatt Spinler std::vector<Repository::AttributesReference> attributes; 568b0a8df5bSMatt Spinler 569b0a8df5bSMatt Spinler std::for_each( 570b0a8df5bSMatt Spinler _pelAttributes.begin(), _pelAttributes.end(), 571b0a8df5bSMatt Spinler [&attributes](auto& pelEntry) { attributes.push_back(pelEntry); }); 572b0a8df5bSMatt Spinler 573b0a8df5bSMatt Spinler std::sort(attributes.begin(), attributes.end(), 574b0a8df5bSMatt Spinler [order](const auto& left, const auto& right) { 575b0a8df5bSMatt Spinler if (order == SortOrder::ascending) 576b0a8df5bSMatt Spinler { 577b0a8df5bSMatt Spinler return left.get().second.path < right.get().second.path; 578b0a8df5bSMatt Spinler } 579b0a8df5bSMatt Spinler return left.get().second.path > right.get().second.path; 580b0a8df5bSMatt Spinler }); 581b0a8df5bSMatt Spinler 582b0a8df5bSMatt Spinler return attributes; 583b0a8df5bSMatt Spinler } 584b0a8df5bSMatt Spinler 585027bf285SSumit Kumar std::vector<uint32_t> 586027bf285SSumit Kumar Repository::prune(const std::vector<uint32_t>& idsWithHwIsoEntry) 587b0a8df5bSMatt Spinler { 588b0a8df5bSMatt Spinler std::vector<uint32_t> obmcLogIDs; 589b0a8df5bSMatt Spinler std::string msg = "Pruning PEL repository that takes up " + 590b0a8df5bSMatt Spinler std::to_string(_sizes.total) + " bytes and has " + 591b0a8df5bSMatt Spinler std::to_string(_pelAttributes.size()) + " PELs"; 592b0a8df5bSMatt Spinler log<level::INFO>(msg.c_str()); 593b0a8df5bSMatt Spinler 594b0a8df5bSMatt Spinler // Set up the 5 functions to check if the PEL category 595b0a8df5bSMatt Spinler // is still over its limits. 596b0a8df5bSMatt Spinler 597b0a8df5bSMatt Spinler // BMC informational PELs should only take up 15% 598b0a8df5bSMatt Spinler IsOverLimitFunc overBMCInfoLimit = [this]() { 599b0a8df5bSMatt Spinler return _sizes.bmcInfo > _maxRepoSize * 15 / 100; 600b0a8df5bSMatt Spinler }; 601b0a8df5bSMatt Spinler 602b0a8df5bSMatt Spinler // BMC non informational PELs should only take up 30% 603b0a8df5bSMatt Spinler IsOverLimitFunc overBMCNonInfoLimit = [this]() { 604b0a8df5bSMatt Spinler return _sizes.bmcServiceable > _maxRepoSize * 30 / 100; 605b0a8df5bSMatt Spinler }; 606b0a8df5bSMatt Spinler 607b0a8df5bSMatt Spinler // Non BMC informational PELs should only take up 15% 608b0a8df5bSMatt Spinler IsOverLimitFunc overNonBMCInfoLimit = [this]() { 609b0a8df5bSMatt Spinler return _sizes.nonBMCInfo > _maxRepoSize * 15 / 100; 610b0a8df5bSMatt Spinler }; 611b0a8df5bSMatt Spinler 612b0a8df5bSMatt Spinler // Non BMC non informational PELs should only take up 15% 613b0a8df5bSMatt Spinler IsOverLimitFunc overNonBMCNonInfoLimit = [this]() { 614b0a8df5bSMatt Spinler return _sizes.nonBMCServiceable > _maxRepoSize * 30 / 100; 615b0a8df5bSMatt Spinler }; 616b0a8df5bSMatt Spinler 617b0a8df5bSMatt Spinler // Bring the total number of PELs down to 80% of the max 618b0a8df5bSMatt Spinler IsOverLimitFunc tooManyPELsLimit = [this]() { 619b0a8df5bSMatt Spinler return _pelAttributes.size() > _maxNumPELs * 80 / 100; 620b0a8df5bSMatt Spinler }; 621b0a8df5bSMatt Spinler 622b0a8df5bSMatt Spinler // Set up the functions to determine which category a PEL is in. 623b0a8df5bSMatt Spinler // TODO: Return false in these functions if a PEL caused a guard record. 624b0a8df5bSMatt Spinler 625b0a8df5bSMatt Spinler // A BMC informational PEL 626b0a8df5bSMatt Spinler IsPELTypeFunc isBMCInfo = [](const PELAttributes& pel) { 627b0a8df5bSMatt Spinler return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) && 628b0a8df5bSMatt Spinler !Repository::isServiceableSev(pel); 629b0a8df5bSMatt Spinler }; 630b0a8df5bSMatt Spinler 631b0a8df5bSMatt Spinler // A BMC non informational PEL 632b0a8df5bSMatt Spinler IsPELTypeFunc isBMCNonInfo = [](const PELAttributes& pel) { 633b0a8df5bSMatt Spinler return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) && 634b0a8df5bSMatt Spinler Repository::isServiceableSev(pel); 635b0a8df5bSMatt Spinler }; 636b0a8df5bSMatt Spinler 637b0a8df5bSMatt Spinler // A non BMC informational PEL 638b0a8df5bSMatt Spinler IsPELTypeFunc isNonBMCInfo = [](const PELAttributes& pel) { 639b0a8df5bSMatt Spinler return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) && 640b0a8df5bSMatt Spinler !Repository::isServiceableSev(pel); 641b0a8df5bSMatt Spinler }; 642b0a8df5bSMatt Spinler 643b0a8df5bSMatt Spinler // A non BMC non informational PEL 644b0a8df5bSMatt Spinler IsPELTypeFunc isNonBMCNonInfo = [](const PELAttributes& pel) { 645b0a8df5bSMatt Spinler return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) && 646b0a8df5bSMatt Spinler Repository::isServiceableSev(pel); 647b0a8df5bSMatt Spinler }; 648b0a8df5bSMatt Spinler 649b0a8df5bSMatt Spinler // When counting PELs, count every PEL 650d26fa3e7SPatrick Williams IsPELTypeFunc isAnyPEL = [](const PELAttributes& /*pel*/) { return true; }; 651b0a8df5bSMatt Spinler 652b0a8df5bSMatt Spinler // Check all 4 categories, which will result in at most 90% 653b0a8df5bSMatt Spinler // usage (15 + 30 + 15 + 30). 654027bf285SSumit Kumar removePELs(overBMCInfoLimit, isBMCInfo, idsWithHwIsoEntry, obmcLogIDs); 655027bf285SSumit Kumar removePELs(overBMCNonInfoLimit, isBMCNonInfo, idsWithHwIsoEntry, 656027bf285SSumit Kumar obmcLogIDs); 657027bf285SSumit Kumar removePELs(overNonBMCInfoLimit, isNonBMCInfo, idsWithHwIsoEntry, 658027bf285SSumit Kumar obmcLogIDs); 659027bf285SSumit Kumar removePELs(overNonBMCNonInfoLimit, isNonBMCNonInfo, idsWithHwIsoEntry, 660027bf285SSumit Kumar obmcLogIDs); 661b0a8df5bSMatt Spinler 662b0a8df5bSMatt Spinler // After the above pruning check if there are still too many PELs, 663b0a8df5bSMatt Spinler // which can happen depending on PEL sizes. 664b0a8df5bSMatt Spinler if (_pelAttributes.size() > _maxNumPELs) 665b0a8df5bSMatt Spinler { 666027bf285SSumit Kumar removePELs(tooManyPELsLimit, isAnyPEL, idsWithHwIsoEntry, obmcLogIDs); 667b0a8df5bSMatt Spinler } 668b0a8df5bSMatt Spinler 669b0a8df5bSMatt Spinler if (!obmcLogIDs.empty()) 670b0a8df5bSMatt Spinler { 67145796e82SMatt Spinler std::string m = "Number of PELs removed to save space: " + 672b0a8df5bSMatt Spinler std::to_string(obmcLogIDs.size()); 67345796e82SMatt Spinler log<level::INFO>(m.c_str()); 674b0a8df5bSMatt Spinler } 675b0a8df5bSMatt Spinler 676b0a8df5bSMatt Spinler return obmcLogIDs; 677b0a8df5bSMatt Spinler } 678b0a8df5bSMatt Spinler 67945796e82SMatt Spinler void Repository::removePELs(const IsOverLimitFunc& isOverLimit, 68045796e82SMatt Spinler const IsPELTypeFunc& isPELType, 681027bf285SSumit Kumar const std::vector<uint32_t>& idsWithHwIsoEntry, 682b0a8df5bSMatt Spinler std::vector<uint32_t>& removedBMCLogIDs) 683b0a8df5bSMatt Spinler { 684b0a8df5bSMatt Spinler if (!isOverLimit()) 685b0a8df5bSMatt Spinler { 686b0a8df5bSMatt Spinler return; 687b0a8df5bSMatt Spinler } 688b0a8df5bSMatt Spinler 689b0a8df5bSMatt Spinler auto attributes = getAllPELAttributes(SortOrder::ascending); 690b0a8df5bSMatt Spinler 691b0a8df5bSMatt Spinler // Make 4 passes on the PELs, stopping as soon as isOverLimit 692b0a8df5bSMatt Spinler // returns false. 693b0a8df5bSMatt Spinler // Pass 1: only delete HMC acked PELs 694b0a8df5bSMatt Spinler // Pass 2: only delete OS acked PELs 695b0a8df5bSMatt Spinler // Pass 3: only delete PHYP sent PELs 696b0a8df5bSMatt Spinler // Pass 4: delete all PELs 697b0a8df5bSMatt Spinler static const std::vector<std::function<bool(const PELAttributes& pel)>> 698b0a8df5bSMatt Spinler stateChecks{[](const auto& pel) { 699b0a8df5bSMatt Spinler return pel.hmcState == TransmissionState::acked; 700b0a8df5bSMatt Spinler }, 701b0a8df5bSMatt Spinler 702b0a8df5bSMatt Spinler [](const auto& pel) { 703b0a8df5bSMatt Spinler return pel.hostState == TransmissionState::acked; 704b0a8df5bSMatt Spinler }, 705b0a8df5bSMatt Spinler 706b0a8df5bSMatt Spinler [](const auto& pel) { 707b0a8df5bSMatt Spinler return pel.hostState == TransmissionState::sent; 708b0a8df5bSMatt Spinler }, 709b0a8df5bSMatt Spinler 710d26fa3e7SPatrick Williams [](const auto& /*pel*/) { return true; }}; 711b0a8df5bSMatt Spinler 712b0a8df5bSMatt Spinler for (const auto& stateCheck : stateChecks) 713b0a8df5bSMatt Spinler { 714b0a8df5bSMatt Spinler for (auto it = attributes.begin(); it != attributes.end();) 715b0a8df5bSMatt Spinler { 716b0a8df5bSMatt Spinler const auto& pel = it->get(); 717b0a8df5bSMatt Spinler if (isPELType(pel.second) && stateCheck(pel.second)) 718b0a8df5bSMatt Spinler { 719b0a8df5bSMatt Spinler auto removedID = pel.first.obmcID.id; 720027bf285SSumit Kumar 721027bf285SSumit Kumar auto idFound = std::find(idsWithHwIsoEntry.begin(), 722027bf285SSumit Kumar idsWithHwIsoEntry.end(), removedID); 723027bf285SSumit Kumar if (idFound != idsWithHwIsoEntry.end()) 724027bf285SSumit Kumar { 725027bf285SSumit Kumar ++it; 726027bf285SSumit Kumar continue; 727027bf285SSumit Kumar } 728027bf285SSumit Kumar 729b0a8df5bSMatt Spinler remove(pel.first); 730b0a8df5bSMatt Spinler 731b0a8df5bSMatt Spinler removedBMCLogIDs.push_back(removedID); 732b0a8df5bSMatt Spinler 733b0a8df5bSMatt Spinler attributes.erase(it); 734b0a8df5bSMatt Spinler 735b0a8df5bSMatt Spinler if (!isOverLimit()) 736b0a8df5bSMatt Spinler { 737b0a8df5bSMatt Spinler break; 738b0a8df5bSMatt Spinler } 739b0a8df5bSMatt Spinler } 740b0a8df5bSMatt Spinler else 741b0a8df5bSMatt Spinler { 742b0a8df5bSMatt Spinler ++it; 743b0a8df5bSMatt Spinler } 744b0a8df5bSMatt Spinler } 745b0a8df5bSMatt Spinler 746b0a8df5bSMatt Spinler if (!isOverLimit()) 747b0a8df5bSMatt Spinler { 748b0a8df5bSMatt Spinler break; 749b0a8df5bSMatt Spinler } 750b0a8df5bSMatt Spinler } 751b0a8df5bSMatt Spinler } 752b0a8df5bSMatt Spinler 7532ccdcef9SSumit Kumar void Repository::archivePEL(const PEL& pel) 7542ccdcef9SSumit Kumar { 7552ccdcef9SSumit Kumar if (pel.valid()) 7562ccdcef9SSumit Kumar { 7572ccdcef9SSumit Kumar auto path = _archivePath / getPELFilename(pel.id(), pel.commitTime()); 7582ccdcef9SSumit Kumar 7592ccdcef9SSumit Kumar write(pel, path); 7602ccdcef9SSumit Kumar 7612ccdcef9SSumit Kumar _archiveSize += getFileDiskSize(path); 7622ccdcef9SSumit Kumar } 7632ccdcef9SSumit Kumar } 7642ccdcef9SSumit Kumar 76589fa082aSMatt Spinler } // namespace pels 76689fa082aSMatt Spinler } // namespace openpower 767