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 <fstream> 2189fa082aSMatt Spinler #include <phosphor-logging/log.hpp> 2289fa082aSMatt Spinler #include <xyz/openbmc_project/Common/File/error.hpp> 2389fa082aSMatt Spinler 2489fa082aSMatt Spinler namespace openpower 2589fa082aSMatt Spinler { 2689fa082aSMatt Spinler namespace pels 2789fa082aSMatt Spinler { 2889fa082aSMatt Spinler 2989fa082aSMatt Spinler namespace fs = std::filesystem; 3089fa082aSMatt Spinler using namespace phosphor::logging; 3189fa082aSMatt Spinler namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error; 3289fa082aSMatt Spinler 337e727a39SMatt Spinler constexpr size_t warningPercentage = 95; 347e727a39SMatt Spinler 35dd325c32SMatt Spinler /** 36dd325c32SMatt Spinler * @brief Returns the amount of space the file uses on disk. 37dd325c32SMatt Spinler * 38dd325c32SMatt Spinler * This is different than just the regular size of the file. 39dd325c32SMatt Spinler * 40dd325c32SMatt Spinler * @param[in] file - The file to get the size of 41dd325c32SMatt Spinler * 42dd325c32SMatt Spinler * @return size_t The disk space the file uses 43dd325c32SMatt Spinler */ 44dd325c32SMatt Spinler size_t getFileDiskSize(const std::filesystem::path& file) 45dd325c32SMatt Spinler { 46dd325c32SMatt Spinler constexpr size_t statBlockSize = 512; 47dd325c32SMatt Spinler struct stat statData; 48dd325c32SMatt Spinler auto rc = stat(file.c_str(), &statData); 49dd325c32SMatt Spinler if (rc != 0) 50dd325c32SMatt Spinler { 51dd325c32SMatt Spinler auto e = errno; 52dd325c32SMatt Spinler std::string msg = "call to stat() failed on " + file.native() + 53dd325c32SMatt Spinler " with errno " + std::to_string(e); 54dd325c32SMatt Spinler log<level::ERR>(msg.c_str()); 55dd325c32SMatt Spinler abort(); 56dd325c32SMatt Spinler } 57dd325c32SMatt Spinler 58dd325c32SMatt Spinler return statData.st_blocks * statBlockSize; 59dd325c32SMatt Spinler } 60dd325c32SMatt Spinler 618d5f3a2bSMatt Spinler Repository::Repository(const std::filesystem::path& basePath, size_t repoSize, 628d5f3a2bSMatt Spinler size_t maxNumPELs) : 638d5f3a2bSMatt Spinler _logPath(basePath / "logs"), 648d5f3a2bSMatt Spinler _maxRepoSize(repoSize), _maxNumPELs(maxNumPELs) 6589fa082aSMatt Spinler { 6689fa082aSMatt Spinler if (!fs::exists(_logPath)) 6789fa082aSMatt Spinler { 6889fa082aSMatt Spinler fs::create_directories(_logPath); 6989fa082aSMatt Spinler } 70475e574dSMatt Spinler 71475e574dSMatt Spinler restore(); 72475e574dSMatt Spinler } 73475e574dSMatt Spinler 74475e574dSMatt Spinler void Repository::restore() 75475e574dSMatt Spinler { 76475e574dSMatt Spinler for (auto& dirEntry : fs::directory_iterator(_logPath)) 77475e574dSMatt Spinler { 78475e574dSMatt Spinler try 79475e574dSMatt Spinler { 80475e574dSMatt Spinler if (!fs::is_regular_file(dirEntry.path())) 81475e574dSMatt Spinler { 82475e574dSMatt Spinler continue; 83475e574dSMatt Spinler } 84475e574dSMatt Spinler 85475e574dSMatt Spinler std::ifstream file{dirEntry.path()}; 86475e574dSMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 87475e574dSMatt Spinler std::istreambuf_iterator<char>()}; 88475e574dSMatt Spinler file.close(); 89475e574dSMatt Spinler 9007eefc54SMatt Spinler PEL pel{data}; 91475e574dSMatt Spinler if (pel.valid()) 92475e574dSMatt Spinler { 93a3c12a48SMatt Spinler // If the host hasn't acked it, reset the host state so 94a3c12a48SMatt Spinler // it will get sent up again. 95a3c12a48SMatt Spinler if (pel.hostTransmissionState() == TransmissionState::sent) 96a3c12a48SMatt Spinler { 97a3c12a48SMatt Spinler pel.setHostTransmissionState(TransmissionState::newPEL); 98a3c12a48SMatt Spinler try 99a3c12a48SMatt Spinler { 100a3c12a48SMatt Spinler write(pel, dirEntry.path()); 101a3c12a48SMatt Spinler } 102a3c12a48SMatt Spinler catch (std::exception& e) 103a3c12a48SMatt Spinler { 104a3c12a48SMatt Spinler log<level::ERR>( 105a3c12a48SMatt Spinler "Failed to save PEL after updating host state", 106a3c12a48SMatt Spinler entry("PELID=0x%X", pel.id())); 107a3c12a48SMatt Spinler } 108a3c12a48SMatt Spinler } 109a3c12a48SMatt Spinler 110dd325c32SMatt Spinler PELAttributes attributes{dirEntry.path(), 111dd325c32SMatt Spinler getFileDiskSize(dirEntry.path()), 112dd325c32SMatt Spinler pel.privateHeader().creatorID(), 113dd325c32SMatt Spinler pel.userHeader().severity(), 114dd325c32SMatt Spinler pel.userHeader().actionFlags(), 115dd325c32SMatt Spinler pel.hostTransmissionState(), 116dd325c32SMatt Spinler pel.hmcTransmissionState()}; 1170ff00485SMatt Spinler 118475e574dSMatt Spinler using pelID = LogID::Pel; 119475e574dSMatt Spinler using obmcID = LogID::Obmc; 1200ff00485SMatt Spinler _pelAttributes.emplace( 121475e574dSMatt Spinler LogID(pelID(pel.id()), obmcID(pel.obmcLogID())), 1220ff00485SMatt Spinler attributes); 123b188f78aSMatt Spinler 124b188f78aSMatt Spinler updateRepoStats(attributes, true); 125475e574dSMatt Spinler } 126475e574dSMatt Spinler else 127475e574dSMatt Spinler { 128475e574dSMatt Spinler log<level::ERR>( 129475e574dSMatt Spinler "Found invalid PEL file while restoring. Removing.", 130475e574dSMatt Spinler entry("FILENAME=%s", dirEntry.path().c_str())); 131475e574dSMatt Spinler fs::remove(dirEntry.path()); 132475e574dSMatt Spinler } 133475e574dSMatt Spinler } 134475e574dSMatt Spinler catch (std::exception& e) 135475e574dSMatt Spinler { 136475e574dSMatt Spinler log<level::ERR>("Hit exception while restoring PEL File", 137475e574dSMatt Spinler entry("FILENAME=%s", dirEntry.path().c_str()), 138475e574dSMatt Spinler entry("ERROR=%s", e.what())); 139475e574dSMatt Spinler } 140475e574dSMatt Spinler } 14189fa082aSMatt Spinler } 14289fa082aSMatt Spinler 14389fa082aSMatt Spinler std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time) 14489fa082aSMatt Spinler { 14589fa082aSMatt Spinler char name[50]; 14689fa082aSMatt Spinler sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", time.yearMSB, 14789fa082aSMatt Spinler time.yearLSB, time.month, time.day, time.hour, time.minutes, 14889fa082aSMatt Spinler time.seconds, time.hundredths, pelID); 14989fa082aSMatt Spinler return std::string{name}; 15089fa082aSMatt Spinler } 15189fa082aSMatt Spinler 15289fa082aSMatt Spinler void Repository::add(std::unique_ptr<PEL>& pel) 15389fa082aSMatt Spinler { 154df43a305SMatt Spinler pel->setHostTransmissionState(TransmissionState::newPEL); 155df43a305SMatt Spinler pel->setHMCTransmissionState(TransmissionState::newPEL); 156df43a305SMatt Spinler 15789fa082aSMatt Spinler auto path = _logPath / getPELFilename(pel->id(), pel->commitTime()); 158ab1b97feSMatt Spinler 159ab1b97feSMatt Spinler write(*(pel.get()), path); 160ab1b97feSMatt Spinler 161dd325c32SMatt Spinler PELAttributes attributes{path, 162dd325c32SMatt Spinler getFileDiskSize(path), 163dd325c32SMatt Spinler pel->privateHeader().creatorID(), 164dd325c32SMatt Spinler pel->userHeader().severity(), 165dd325c32SMatt Spinler pel->userHeader().actionFlags(), 166346f99a1SMatt Spinler pel->hostTransmissionState(), 167346f99a1SMatt Spinler pel->hmcTransmissionState()}; 168ab1b97feSMatt Spinler 169ab1b97feSMatt Spinler using pelID = LogID::Pel; 170ab1b97feSMatt Spinler using obmcID = LogID::Obmc; 171ab1b97feSMatt Spinler _pelAttributes.emplace(LogID(pelID(pel->id()), obmcID(pel->obmcLogID())), 172ab1b97feSMatt Spinler attributes); 173ab1b97feSMatt Spinler 174*44893cc9SMatt Spinler _lastPelID = pel->id(); 175*44893cc9SMatt Spinler 176b188f78aSMatt Spinler updateRepoStats(attributes, true); 177b188f78aSMatt Spinler 178ab1b97feSMatt Spinler processAddCallbacks(*pel); 179ab1b97feSMatt Spinler } 180ab1b97feSMatt Spinler 181ab1b97feSMatt Spinler void Repository::write(const PEL& pel, const fs::path& path) 182ab1b97feSMatt Spinler { 18389fa082aSMatt Spinler std::ofstream file{path, std::ios::binary}; 18489fa082aSMatt Spinler 18589fa082aSMatt Spinler if (!file.good()) 18689fa082aSMatt Spinler { 18789fa082aSMatt Spinler // If this fails, the filesystem is probably full so it isn't like 18889fa082aSMatt Spinler // we could successfully create yet another error log here. 18989fa082aSMatt Spinler auto e = errno; 19089fa082aSMatt Spinler fs::remove(path); 19189fa082aSMatt Spinler log<level::ERR>("Unable to open PEL file for writing", 19289fa082aSMatt Spinler entry("ERRNO=%d", e), entry("PATH=%s", path.c_str())); 19389fa082aSMatt Spinler throw file_error::Open(); 19489fa082aSMatt Spinler } 19589fa082aSMatt Spinler 196ab1b97feSMatt Spinler auto data = pel.data(); 19789fa082aSMatt Spinler file.write(reinterpret_cast<const char*>(data.data()), data.size()); 19889fa082aSMatt Spinler 19989fa082aSMatt Spinler if (file.fail()) 20089fa082aSMatt Spinler { 20189fa082aSMatt Spinler // Same note as above about not being able to create an error log 20289fa082aSMatt Spinler // for this case even if we wanted. 20389fa082aSMatt Spinler auto e = errno; 20489fa082aSMatt Spinler file.close(); 20589fa082aSMatt Spinler fs::remove(path); 20689fa082aSMatt Spinler log<level::ERR>("Unable to write PEL file", entry("ERRNO=%d", e), 20789fa082aSMatt Spinler entry("PATH=%s", path.c_str())); 20889fa082aSMatt Spinler throw file_error::Write(); 20989fa082aSMatt Spinler } 210475e574dSMatt Spinler } 211475e574dSMatt Spinler 21252602e35SMatt Spinler std::optional<Repository::LogID> Repository::remove(const LogID& id) 213475e574dSMatt Spinler { 21452602e35SMatt Spinler std::optional<LogID> actualID; 21552602e35SMatt Spinler 216475e574dSMatt Spinler auto pel = findPEL(id); 2170ff00485SMatt Spinler if (pel != _pelAttributes.end()) 218475e574dSMatt Spinler { 21952602e35SMatt Spinler actualID = pel->first; 220b188f78aSMatt Spinler updateRepoStats(pel->second, false); 221b188f78aSMatt Spinler 2225f5352e5SMatt Spinler log<level::DEBUG>("Removing PEL from repository", 2235f5352e5SMatt Spinler entry("PEL_ID=0x%X", pel->first.pelID.id), 2245f5352e5SMatt Spinler entry("OBMC_LOG_ID=%d", pel->first.obmcID.id)); 2250ff00485SMatt Spinler fs::remove(pel->second.path); 2260ff00485SMatt Spinler _pelAttributes.erase(pel); 227421f6531SMatt Spinler 2285f5352e5SMatt Spinler processDeleteCallbacks(pel->first.pelID.id); 2295f5352e5SMatt Spinler } 23052602e35SMatt Spinler 23152602e35SMatt Spinler return actualID; 23289fa082aSMatt Spinler } 23389fa082aSMatt Spinler 2342813f36dSMatt Spinler std::optional<std::vector<uint8_t>> Repository::getPELData(const LogID& id) 2352813f36dSMatt Spinler { 2362813f36dSMatt Spinler auto pel = findPEL(id); 2370ff00485SMatt Spinler if (pel != _pelAttributes.end()) 2382813f36dSMatt Spinler { 2390ff00485SMatt Spinler std::ifstream file{pel->second.path.c_str()}; 2402813f36dSMatt Spinler if (!file.good()) 2412813f36dSMatt Spinler { 2422813f36dSMatt Spinler auto e = errno; 2432813f36dSMatt Spinler log<level::ERR>("Unable to open PEL file", entry("ERRNO=%d", e), 2440ff00485SMatt Spinler entry("PATH=%s", pel->second.path.c_str())); 2452813f36dSMatt Spinler throw file_error::Open(); 2462813f36dSMatt Spinler } 2472813f36dSMatt Spinler 2482813f36dSMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 2492813f36dSMatt Spinler std::istreambuf_iterator<char>()}; 2502813f36dSMatt Spinler return data; 2512813f36dSMatt Spinler } 2522813f36dSMatt Spinler 2532813f36dSMatt Spinler return std::nullopt; 2542813f36dSMatt Spinler } 2552813f36dSMatt Spinler 2566d51224bSMatt Spinler std::optional<sdbusplus::message::unix_fd> Repository::getPELFD(const LogID& id) 2576d51224bSMatt Spinler { 2586d51224bSMatt Spinler auto pel = findPEL(id); 2596d51224bSMatt Spinler if (pel != _pelAttributes.end()) 2606d51224bSMatt Spinler { 2616d51224bSMatt Spinler FILE* fp = fopen(pel->second.path.c_str(), "rb"); 2626d51224bSMatt Spinler 2636d51224bSMatt Spinler if (fp == nullptr) 2646d51224bSMatt Spinler { 2656d51224bSMatt Spinler auto e = errno; 2666d51224bSMatt Spinler log<level::ERR>("Unable to open PEL File", entry("ERRNO=%d", e), 2676d51224bSMatt Spinler entry("PATH=%s", pel->second.path.c_str())); 2686d51224bSMatt Spinler throw file_error::Open(); 2696d51224bSMatt Spinler } 2706d51224bSMatt Spinler 2716d51224bSMatt Spinler // Must leave the file open here. It will be closed by sdbusplus 2726d51224bSMatt Spinler // when it sends it back over D-Bus. 2736d51224bSMatt Spinler 2746d51224bSMatt Spinler return fileno(fp); 2756d51224bSMatt Spinler } 2766d51224bSMatt Spinler return std::nullopt; 2776d51224bSMatt Spinler } 2786d51224bSMatt Spinler 2791ea78801SMatt Spinler void Repository::for_each(ForEachFunc func) const 2801ea78801SMatt Spinler { 2810ff00485SMatt Spinler for (const auto& [id, attributes] : _pelAttributes) 2821ea78801SMatt Spinler { 2830ff00485SMatt Spinler std::ifstream file{attributes.path}; 2841ea78801SMatt Spinler 2851ea78801SMatt Spinler if (!file.good()) 2861ea78801SMatt Spinler { 2871ea78801SMatt Spinler auto e = errno; 2881ea78801SMatt Spinler log<level::ERR>("Repository::for_each: Unable to open PEL file", 2891ea78801SMatt Spinler entry("ERRNO=%d", e), 2900ff00485SMatt Spinler entry("PATH=%s", attributes.path.c_str())); 2911ea78801SMatt Spinler continue; 2921ea78801SMatt Spinler } 2931ea78801SMatt Spinler 2941ea78801SMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 2951ea78801SMatt Spinler std::istreambuf_iterator<char>()}; 2961ea78801SMatt Spinler file.close(); 2971ea78801SMatt Spinler 2981ea78801SMatt Spinler PEL pel{data}; 2991ea78801SMatt Spinler 3001ea78801SMatt Spinler try 3011ea78801SMatt Spinler { 3021ea78801SMatt Spinler if (func(pel)) 3031ea78801SMatt Spinler { 3041ea78801SMatt Spinler break; 3051ea78801SMatt Spinler } 3061ea78801SMatt Spinler } 3071ea78801SMatt Spinler catch (std::exception& e) 3081ea78801SMatt Spinler { 3091ea78801SMatt Spinler log<level::ERR>("Repository::for_each function exception", 3101ea78801SMatt Spinler entry("ERROR=%s", e.what())); 3111ea78801SMatt Spinler } 3121ea78801SMatt Spinler } 3131ea78801SMatt Spinler } 3141ea78801SMatt Spinler 315421f6531SMatt Spinler void Repository::processAddCallbacks(const PEL& pel) const 316421f6531SMatt Spinler { 317421f6531SMatt Spinler for (auto& [name, func] : _addSubscriptions) 318421f6531SMatt Spinler { 319421f6531SMatt Spinler try 320421f6531SMatt Spinler { 321421f6531SMatt Spinler func(pel); 322421f6531SMatt Spinler } 323421f6531SMatt Spinler catch (std::exception& e) 324421f6531SMatt Spinler { 325421f6531SMatt Spinler log<level::ERR>("PEL Repository add callback exception", 326421f6531SMatt Spinler entry("NAME=%s", name.c_str()), 327421f6531SMatt Spinler entry("ERROR=%s", e.what())); 328421f6531SMatt Spinler } 329421f6531SMatt Spinler } 330421f6531SMatt Spinler } 331421f6531SMatt Spinler 332421f6531SMatt Spinler void Repository::processDeleteCallbacks(uint32_t id) const 333421f6531SMatt Spinler { 334421f6531SMatt Spinler for (auto& [name, func] : _deleteSubscriptions) 335421f6531SMatt Spinler { 336421f6531SMatt Spinler try 337421f6531SMatt Spinler { 338421f6531SMatt Spinler func(id); 339421f6531SMatt Spinler } 340421f6531SMatt Spinler catch (std::exception& e) 341421f6531SMatt Spinler { 342421f6531SMatt Spinler log<level::ERR>("PEL Repository delete callback exception", 343421f6531SMatt Spinler entry("NAME=%s", name.c_str()), 344421f6531SMatt Spinler entry("ERROR=%s", e.what())); 345421f6531SMatt Spinler } 346421f6531SMatt Spinler } 347421f6531SMatt Spinler } 348421f6531SMatt Spinler 3490ff00485SMatt Spinler std::optional<std::reference_wrapper<const Repository::PELAttributes>> 3500ff00485SMatt Spinler Repository::getPELAttributes(const LogID& id) const 3510ff00485SMatt Spinler { 3520ff00485SMatt Spinler auto pel = findPEL(id); 3530ff00485SMatt Spinler if (pel != _pelAttributes.end()) 3540ff00485SMatt Spinler { 3550ff00485SMatt Spinler return pel->second; 3560ff00485SMatt Spinler } 3570ff00485SMatt Spinler 3580ff00485SMatt Spinler return std::nullopt; 3590ff00485SMatt Spinler } 3600ff00485SMatt Spinler 36129d18c11SMatt Spinler void Repository::setPELHostTransState(uint32_t pelID, TransmissionState state) 36229d18c11SMatt Spinler { 36329d18c11SMatt Spinler LogID id{LogID::Pel{pelID}}; 36429d18c11SMatt Spinler auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 36529d18c11SMatt Spinler [&id](const auto& a) { return a.first == id; }); 36629d18c11SMatt Spinler 36729d18c11SMatt Spinler if ((attr != _pelAttributes.end()) && (attr->second.hostState != state)) 36829d18c11SMatt Spinler { 36929d18c11SMatt Spinler PELUpdateFunc func = [state](PEL& pel) { 37029d18c11SMatt Spinler pel.setHostTransmissionState(state); 37129d18c11SMatt Spinler }; 37229d18c11SMatt Spinler 37329d18c11SMatt Spinler try 37429d18c11SMatt Spinler { 37529d18c11SMatt Spinler updatePEL(attr->second.path, func); 37629d18c11SMatt Spinler 37729d18c11SMatt Spinler attr->second.hostState = state; 37829d18c11SMatt Spinler } 37929d18c11SMatt Spinler catch (std::exception& e) 38029d18c11SMatt Spinler { 38129d18c11SMatt Spinler log<level::ERR>("Unable to update PEL host transmission state", 38229d18c11SMatt Spinler entry("PATH=%s", attr->second.path.c_str()), 38329d18c11SMatt Spinler entry("ERROR=%s", e.what())); 38429d18c11SMatt Spinler } 38529d18c11SMatt Spinler } 38629d18c11SMatt Spinler } 38729d18c11SMatt Spinler 38829d18c11SMatt Spinler void Repository::setPELHMCTransState(uint32_t pelID, TransmissionState state) 38929d18c11SMatt Spinler { 39029d18c11SMatt Spinler LogID id{LogID::Pel{pelID}}; 39129d18c11SMatt Spinler auto attr = std::find_if(_pelAttributes.begin(), _pelAttributes.end(), 39229d18c11SMatt Spinler [&id](const auto& a) { return a.first == id; }); 39329d18c11SMatt Spinler 39429d18c11SMatt Spinler if ((attr != _pelAttributes.end()) && (attr->second.hmcState != state)) 39529d18c11SMatt Spinler { 39629d18c11SMatt Spinler PELUpdateFunc func = [state](PEL& pel) { 39729d18c11SMatt Spinler pel.setHMCTransmissionState(state); 39829d18c11SMatt Spinler }; 39929d18c11SMatt Spinler 40029d18c11SMatt Spinler try 40129d18c11SMatt Spinler { 40229d18c11SMatt Spinler updatePEL(attr->second.path, func); 40329d18c11SMatt Spinler 40429d18c11SMatt Spinler attr->second.hmcState = state; 40529d18c11SMatt Spinler } 40629d18c11SMatt Spinler catch (std::exception& e) 40729d18c11SMatt Spinler { 40829d18c11SMatt Spinler log<level::ERR>("Unable to update PEL HMC transmission state", 40929d18c11SMatt Spinler entry("PATH=%s", attr->second.path.c_str()), 41029d18c11SMatt Spinler entry("ERROR=%s", e.what())); 41129d18c11SMatt Spinler } 41229d18c11SMatt Spinler } 41329d18c11SMatt Spinler } 41429d18c11SMatt Spinler 41529d18c11SMatt Spinler void Repository::updatePEL(const fs::path& path, PELUpdateFunc updateFunc) 41629d18c11SMatt Spinler { 41729d18c11SMatt Spinler std::ifstream file{path}; 41829d18c11SMatt Spinler std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 41929d18c11SMatt Spinler std::istreambuf_iterator<char>()}; 42029d18c11SMatt Spinler file.close(); 42129d18c11SMatt Spinler 42229d18c11SMatt Spinler PEL pel{data}; 42329d18c11SMatt Spinler 42429d18c11SMatt Spinler if (pel.valid()) 42529d18c11SMatt Spinler { 42629d18c11SMatt Spinler updateFunc(pel); 42729d18c11SMatt Spinler 42829d18c11SMatt Spinler write(pel, path); 42929d18c11SMatt Spinler } 43029d18c11SMatt Spinler else 43129d18c11SMatt Spinler { 43229d18c11SMatt Spinler throw std::runtime_error( 43329d18c11SMatt Spinler "Unable to read a valid PEL when trying to update it"); 43429d18c11SMatt Spinler } 43529d18c11SMatt Spinler } 43629d18c11SMatt Spinler 437b188f78aSMatt Spinler bool Repository::isServiceableSev(const PELAttributes& pel) 438b188f78aSMatt Spinler { 439b188f78aSMatt Spinler auto sevType = static_cast<SeverityType>(pel.severity & 0xF0); 440b188f78aSMatt Spinler auto sevPVEntry = 441b188f78aSMatt Spinler pel_values::findByValue(pel.severity, pel_values::severityValues); 442b188f78aSMatt Spinler std::string sevName = std::get<pel_values::registryNamePos>(*sevPVEntry); 443b188f78aSMatt Spinler 444b188f78aSMatt Spinler bool check1 = (sevType == SeverityType::predictive) || 445b188f78aSMatt Spinler (sevType == SeverityType::unrecoverable) || 446b188f78aSMatt Spinler (sevType == SeverityType::critical); 447b188f78aSMatt Spinler 448b188f78aSMatt Spinler bool check2 = ((sevType == SeverityType::recovered) || 449b188f78aSMatt Spinler (sevName == "symptom_recovered")) && 450b188f78aSMatt Spinler !pel.actionFlags.test(hiddenFlagBit); 451b188f78aSMatt Spinler 452b188f78aSMatt Spinler bool check3 = (sevName == "symptom_predictive") || 453b188f78aSMatt Spinler (sevName == "symptom_unrecoverable") || 454b188f78aSMatt Spinler (sevName == "symptom_critical"); 455b188f78aSMatt Spinler 456b188f78aSMatt Spinler return check1 || check2 || check3; 457b188f78aSMatt Spinler } 458b188f78aSMatt Spinler 459b188f78aSMatt Spinler void Repository::updateRepoStats(const PELAttributes& pel, bool pelAdded) 460b188f78aSMatt Spinler { 461b188f78aSMatt Spinler auto isServiceable = Repository::isServiceableSev(pel); 462b188f78aSMatt Spinler auto bmcPEL = CreatorID::openBMC == static_cast<CreatorID>(pel.creator); 463b188f78aSMatt Spinler 464b188f78aSMatt Spinler auto adjustSize = [pelAdded, &pel](auto& runningSize) { 465b188f78aSMatt Spinler if (pelAdded) 466b188f78aSMatt Spinler { 467b188f78aSMatt Spinler runningSize += pel.sizeOnDisk; 468b188f78aSMatt Spinler } 469b188f78aSMatt Spinler else 470b188f78aSMatt Spinler { 471b188f78aSMatt Spinler runningSize = std::max(static_cast<int64_t>(runningSize) - 472b188f78aSMatt Spinler static_cast<int64_t>(pel.sizeOnDisk), 473b188f78aSMatt Spinler static_cast<int64_t>(0)); 474b188f78aSMatt Spinler } 475b188f78aSMatt Spinler }; 476b188f78aSMatt Spinler 477b188f78aSMatt Spinler adjustSize(_sizes.total); 478b188f78aSMatt Spinler 479b188f78aSMatt Spinler if (bmcPEL) 480b188f78aSMatt Spinler { 481b188f78aSMatt Spinler adjustSize(_sizes.bmc); 482b188f78aSMatt Spinler if (isServiceable) 483b188f78aSMatt Spinler { 484b188f78aSMatt Spinler adjustSize(_sizes.bmcServiceable); 485b188f78aSMatt Spinler } 486b188f78aSMatt Spinler else 487b188f78aSMatt Spinler { 488b188f78aSMatt Spinler adjustSize(_sizes.bmcInfo); 489b188f78aSMatt Spinler } 490b188f78aSMatt Spinler } 491b188f78aSMatt Spinler else 492b188f78aSMatt Spinler { 493b188f78aSMatt Spinler adjustSize(_sizes.nonBMC); 494b188f78aSMatt Spinler if (isServiceable) 495b188f78aSMatt Spinler { 496b188f78aSMatt Spinler adjustSize(_sizes.nonBMCServiceable); 497b188f78aSMatt Spinler } 498b188f78aSMatt Spinler else 499b188f78aSMatt Spinler { 500b188f78aSMatt Spinler adjustSize(_sizes.nonBMCInfo); 501b188f78aSMatt Spinler } 502b188f78aSMatt Spinler } 503b188f78aSMatt Spinler } 504b188f78aSMatt Spinler 5057e727a39SMatt Spinler bool Repository::sizeWarning() const 5067e727a39SMatt Spinler { 5077e727a39SMatt Spinler return (_sizes.total > (_maxRepoSize * warningPercentage / 100)) || 5087e727a39SMatt Spinler (_pelAttributes.size() > _maxNumPELs); 5097e727a39SMatt Spinler } 5107e727a39SMatt Spinler 511b0a8df5bSMatt Spinler std::vector<Repository::AttributesReference> 512b0a8df5bSMatt Spinler Repository::getAllPELAttributes(SortOrder order) const 513b0a8df5bSMatt Spinler { 514b0a8df5bSMatt Spinler std::vector<Repository::AttributesReference> attributes; 515b0a8df5bSMatt Spinler 516b0a8df5bSMatt Spinler std::for_each( 517b0a8df5bSMatt Spinler _pelAttributes.begin(), _pelAttributes.end(), 518b0a8df5bSMatt Spinler [&attributes](auto& pelEntry) { attributes.push_back(pelEntry); }); 519b0a8df5bSMatt Spinler 520b0a8df5bSMatt Spinler std::sort(attributes.begin(), attributes.end(), 521b0a8df5bSMatt Spinler [order](const auto& left, const auto& right) { 522b0a8df5bSMatt Spinler if (order == SortOrder::ascending) 523b0a8df5bSMatt Spinler { 524b0a8df5bSMatt Spinler return left.get().second.path < right.get().second.path; 525b0a8df5bSMatt Spinler } 526b0a8df5bSMatt Spinler return left.get().second.path > right.get().second.path; 527b0a8df5bSMatt Spinler }); 528b0a8df5bSMatt Spinler 529b0a8df5bSMatt Spinler return attributes; 530b0a8df5bSMatt Spinler } 531b0a8df5bSMatt Spinler 532b0a8df5bSMatt Spinler std::vector<uint32_t> Repository::prune() 533b0a8df5bSMatt Spinler { 534b0a8df5bSMatt Spinler std::vector<uint32_t> obmcLogIDs; 535b0a8df5bSMatt Spinler std::string msg = "Pruning PEL repository that takes up " + 536b0a8df5bSMatt Spinler std::to_string(_sizes.total) + " bytes and has " + 537b0a8df5bSMatt Spinler std::to_string(_pelAttributes.size()) + " PELs"; 538b0a8df5bSMatt Spinler log<level::INFO>(msg.c_str()); 539b0a8df5bSMatt Spinler 540b0a8df5bSMatt Spinler // Set up the 5 functions to check if the PEL category 541b0a8df5bSMatt Spinler // is still over its limits. 542b0a8df5bSMatt Spinler 543b0a8df5bSMatt Spinler // BMC informational PELs should only take up 15% 544b0a8df5bSMatt Spinler IsOverLimitFunc overBMCInfoLimit = [this]() { 545b0a8df5bSMatt Spinler return _sizes.bmcInfo > _maxRepoSize * 15 / 100; 546b0a8df5bSMatt Spinler }; 547b0a8df5bSMatt Spinler 548b0a8df5bSMatt Spinler // BMC non informational PELs should only take up 30% 549b0a8df5bSMatt Spinler IsOverLimitFunc overBMCNonInfoLimit = [this]() { 550b0a8df5bSMatt Spinler return _sizes.bmcServiceable > _maxRepoSize * 30 / 100; 551b0a8df5bSMatt Spinler }; 552b0a8df5bSMatt Spinler 553b0a8df5bSMatt Spinler // Non BMC informational PELs should only take up 15% 554b0a8df5bSMatt Spinler IsOverLimitFunc overNonBMCInfoLimit = [this]() { 555b0a8df5bSMatt Spinler return _sizes.nonBMCInfo > _maxRepoSize * 15 / 100; 556b0a8df5bSMatt Spinler }; 557b0a8df5bSMatt Spinler 558b0a8df5bSMatt Spinler // Non BMC non informational PELs should only take up 15% 559b0a8df5bSMatt Spinler IsOverLimitFunc overNonBMCNonInfoLimit = [this]() { 560b0a8df5bSMatt Spinler return _sizes.nonBMCServiceable > _maxRepoSize * 30 / 100; 561b0a8df5bSMatt Spinler }; 562b0a8df5bSMatt Spinler 563b0a8df5bSMatt Spinler // Bring the total number of PELs down to 80% of the max 564b0a8df5bSMatt Spinler IsOverLimitFunc tooManyPELsLimit = [this]() { 565b0a8df5bSMatt Spinler return _pelAttributes.size() > _maxNumPELs * 80 / 100; 566b0a8df5bSMatt Spinler }; 567b0a8df5bSMatt Spinler 568b0a8df5bSMatt Spinler // Set up the functions to determine which category a PEL is in. 569b0a8df5bSMatt Spinler // TODO: Return false in these functions if a PEL caused a guard record. 570b0a8df5bSMatt Spinler 571b0a8df5bSMatt Spinler // A BMC informational PEL 572b0a8df5bSMatt Spinler IsPELTypeFunc isBMCInfo = [](const PELAttributes& pel) { 573b0a8df5bSMatt Spinler return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) && 574b0a8df5bSMatt Spinler !Repository::isServiceableSev(pel); 575b0a8df5bSMatt Spinler }; 576b0a8df5bSMatt Spinler 577b0a8df5bSMatt Spinler // A BMC non informational PEL 578b0a8df5bSMatt Spinler IsPELTypeFunc isBMCNonInfo = [](const PELAttributes& pel) { 579b0a8df5bSMatt Spinler return (CreatorID::openBMC == static_cast<CreatorID>(pel.creator)) && 580b0a8df5bSMatt Spinler Repository::isServiceableSev(pel); 581b0a8df5bSMatt Spinler }; 582b0a8df5bSMatt Spinler 583b0a8df5bSMatt Spinler // A non BMC informational PEL 584b0a8df5bSMatt Spinler IsPELTypeFunc isNonBMCInfo = [](const PELAttributes& pel) { 585b0a8df5bSMatt Spinler return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) && 586b0a8df5bSMatt Spinler !Repository::isServiceableSev(pel); 587b0a8df5bSMatt Spinler }; 588b0a8df5bSMatt Spinler 589b0a8df5bSMatt Spinler // A non BMC non informational PEL 590b0a8df5bSMatt Spinler IsPELTypeFunc isNonBMCNonInfo = [](const PELAttributes& pel) { 591b0a8df5bSMatt Spinler return (CreatorID::openBMC != static_cast<CreatorID>(pel.creator)) && 592b0a8df5bSMatt Spinler Repository::isServiceableSev(pel); 593b0a8df5bSMatt Spinler }; 594b0a8df5bSMatt Spinler 595b0a8df5bSMatt Spinler // When counting PELs, count every PEL 596b0a8df5bSMatt Spinler IsPELTypeFunc isAnyPEL = [](const PELAttributes& pel) { return true; }; 597b0a8df5bSMatt Spinler 598b0a8df5bSMatt Spinler // Check all 4 categories, which will result in at most 90% 599b0a8df5bSMatt Spinler // usage (15 + 30 + 15 + 30). 600b0a8df5bSMatt Spinler removePELs(overBMCInfoLimit, isBMCInfo, obmcLogIDs); 601b0a8df5bSMatt Spinler removePELs(overBMCNonInfoLimit, isBMCNonInfo, obmcLogIDs); 602b0a8df5bSMatt Spinler removePELs(overNonBMCInfoLimit, isNonBMCInfo, obmcLogIDs); 603b0a8df5bSMatt Spinler removePELs(overNonBMCNonInfoLimit, isNonBMCNonInfo, obmcLogIDs); 604b0a8df5bSMatt Spinler 605b0a8df5bSMatt Spinler // After the above pruning check if there are still too many PELs, 606b0a8df5bSMatt Spinler // which can happen depending on PEL sizes. 607b0a8df5bSMatt Spinler if (_pelAttributes.size() > _maxNumPELs) 608b0a8df5bSMatt Spinler { 609b0a8df5bSMatt Spinler removePELs(tooManyPELsLimit, isAnyPEL, obmcLogIDs); 610b0a8df5bSMatt Spinler } 611b0a8df5bSMatt Spinler 612b0a8df5bSMatt Spinler if (!obmcLogIDs.empty()) 613b0a8df5bSMatt Spinler { 614b0a8df5bSMatt Spinler std::string msg = "Number of PELs removed to save space: " + 615b0a8df5bSMatt Spinler std::to_string(obmcLogIDs.size()); 616b0a8df5bSMatt Spinler log<level::INFO>(msg.c_str()); 617b0a8df5bSMatt Spinler } 618b0a8df5bSMatt Spinler 619b0a8df5bSMatt Spinler return obmcLogIDs; 620b0a8df5bSMatt Spinler } 621b0a8df5bSMatt Spinler 622b0a8df5bSMatt Spinler void Repository::removePELs(IsOverLimitFunc& isOverLimit, 623b0a8df5bSMatt Spinler IsPELTypeFunc& isPELType, 624b0a8df5bSMatt Spinler std::vector<uint32_t>& removedBMCLogIDs) 625b0a8df5bSMatt Spinler { 626b0a8df5bSMatt Spinler if (!isOverLimit()) 627b0a8df5bSMatt Spinler { 628b0a8df5bSMatt Spinler return; 629b0a8df5bSMatt Spinler } 630b0a8df5bSMatt Spinler 631b0a8df5bSMatt Spinler auto attributes = getAllPELAttributes(SortOrder::ascending); 632b0a8df5bSMatt Spinler 633b0a8df5bSMatt Spinler // Make 4 passes on the PELs, stopping as soon as isOverLimit 634b0a8df5bSMatt Spinler // returns false. 635b0a8df5bSMatt Spinler // Pass 1: only delete HMC acked PELs 636b0a8df5bSMatt Spinler // Pass 2: only delete OS acked PELs 637b0a8df5bSMatt Spinler // Pass 3: only delete PHYP sent PELs 638b0a8df5bSMatt Spinler // Pass 4: delete all PELs 639b0a8df5bSMatt Spinler static const std::vector<std::function<bool(const PELAttributes& pel)>> 640b0a8df5bSMatt Spinler stateChecks{[](const auto& pel) { 641b0a8df5bSMatt Spinler return pel.hmcState == TransmissionState::acked; 642b0a8df5bSMatt Spinler }, 643b0a8df5bSMatt Spinler 644b0a8df5bSMatt Spinler [](const auto& pel) { 645b0a8df5bSMatt Spinler return pel.hostState == TransmissionState::acked; 646b0a8df5bSMatt Spinler }, 647b0a8df5bSMatt Spinler 648b0a8df5bSMatt Spinler [](const auto& pel) { 649b0a8df5bSMatt Spinler return pel.hostState == TransmissionState::sent; 650b0a8df5bSMatt Spinler }, 651b0a8df5bSMatt Spinler 652b0a8df5bSMatt Spinler [](const auto& pel) { return true; }}; 653b0a8df5bSMatt Spinler 654b0a8df5bSMatt Spinler for (const auto& stateCheck : stateChecks) 655b0a8df5bSMatt Spinler { 656b0a8df5bSMatt Spinler for (auto it = attributes.begin(); it != attributes.end();) 657b0a8df5bSMatt Spinler { 658b0a8df5bSMatt Spinler const auto& pel = it->get(); 659b0a8df5bSMatt Spinler if (isPELType(pel.second) && stateCheck(pel.second)) 660b0a8df5bSMatt Spinler { 661b0a8df5bSMatt Spinler auto removedID = pel.first.obmcID.id; 662b0a8df5bSMatt Spinler remove(pel.first); 663b0a8df5bSMatt Spinler 664b0a8df5bSMatt Spinler removedBMCLogIDs.push_back(removedID); 665b0a8df5bSMatt Spinler 666b0a8df5bSMatt Spinler attributes.erase(it); 667b0a8df5bSMatt Spinler 668b0a8df5bSMatt Spinler if (!isOverLimit()) 669b0a8df5bSMatt Spinler { 670b0a8df5bSMatt Spinler break; 671b0a8df5bSMatt Spinler } 672b0a8df5bSMatt Spinler } 673b0a8df5bSMatt Spinler else 674b0a8df5bSMatt Spinler { 675b0a8df5bSMatt Spinler ++it; 676b0a8df5bSMatt Spinler } 677b0a8df5bSMatt Spinler } 678b0a8df5bSMatt Spinler 679b0a8df5bSMatt Spinler if (!isOverLimit()) 680b0a8df5bSMatt Spinler { 681b0a8df5bSMatt Spinler break; 682b0a8df5bSMatt Spinler } 683b0a8df5bSMatt Spinler } 684b0a8df5bSMatt Spinler } 685b0a8df5bSMatt Spinler 68689fa082aSMatt Spinler } // namespace pels 68789fa082aSMatt Spinler } // namespace openpower 688