1f60ac27eSMatt Spinler #pragma once 2f60ac27eSMatt Spinler 3f60ac27eSMatt Spinler #include "host_interface.hpp" 4f60ac27eSMatt Spinler #include "pel.hpp" 5f60ac27eSMatt Spinler #include "repository.hpp" 6f60ac27eSMatt Spinler 7f869fcf8SMatt Spinler #include <sdeventplus/clock.hpp> 87d800a4eSMatt Spinler #include <sdeventplus/source/event.hpp> 9f869fcf8SMatt Spinler #include <sdeventplus/utility/timer.hpp> 10f60ac27eSMatt Spinler 11*2544b419SPatrick Williams #include <deque> 12*2544b419SPatrick Williams 13f60ac27eSMatt Spinler namespace openpower::pels 14f60ac27eSMatt Spinler { 15f60ac27eSMatt Spinler 16f60ac27eSMatt Spinler /** 17f60ac27eSMatt Spinler * @class HostNotifier 18f60ac27eSMatt Spinler * 19f60ac27eSMatt Spinler * This class handles notifying the host firmware of new PELs. 200e1593ecSMatt Spinler * 210e1593ecSMatt Spinler * It uses the Repository class's subscription feature to be 220e1593ecSMatt Spinler * notified about new PELs. 230e1593ecSMatt Spinler * 240e1593ecSMatt Spinler * Some PELs do not need to be sent - see enqueueRequired() and 250e1593ecSMatt Spinler * notifyRequired(). 260e1593ecSMatt Spinler * 270e1593ecSMatt Spinler * The high level good path flow for sending a single PEL is: 280e1593ecSMatt Spinler * 290e1593ecSMatt Spinler * 1) Send the ID and size of the new PEL to the host. 300e1593ecSMatt Spinler * - The command response is asynchronous. 310e1593ecSMatt Spinler * 320e1593ecSMatt Spinler * 2) The host reads the raw PEL data (outside of this class). 330e1593ecSMatt Spinler * 340e1593ecSMatt Spinler * 3) The host sends the PEL to the OS, and then sends an AckPEL 350e1593ecSMatt Spinler * PLDM command to the PLDM daemon, who makes a D-Bus method 360e1593ecSMatt Spinler * call to this daemon, which calls HostNotifer::ackPEL(). 370e1593ecSMatt Spinler * 380e1593ecSMatt Spinler * After this, a PEL never needs to be sent again, but if the 390e1593ecSMatt Spinler * host is rebooted before the ack comes it will. 400e1593ecSMatt Spinler * 410e1593ecSMatt Spinler * The host firmware has a finite amount of space to store PELs before 420e1593ecSMatt Spinler * sending to the OS, and it's possible it will fill up. In this case, 430e1593ecSMatt Spinler * the AckPEL command will have a special response that will tell the 440e1593ecSMatt Spinler * PLDM daemon to call HostReject D-Bus method on this daemon instead 450e1593ecSMatt Spinler * which will invoke HostNotifier::setHostFull(). This will stop new 460e1593ecSMatt Spinler * PELs from being sent, and the first PEL that hits this will have 470e1593ecSMatt Spinler * a timer set to retry again later. 48f60ac27eSMatt Spinler */ 49f60ac27eSMatt Spinler class HostNotifier 50f60ac27eSMatt Spinler { 51f60ac27eSMatt Spinler public: 52f60ac27eSMatt Spinler HostNotifier() = delete; 53f60ac27eSMatt Spinler HostNotifier(const HostNotifier&) = delete; 54f60ac27eSMatt Spinler HostNotifier& operator=(const HostNotifier&) = delete; 55f60ac27eSMatt Spinler HostNotifier(HostNotifier&&) = delete; 56f60ac27eSMatt Spinler HostNotifier& operator=(HostNotifier&&) = delete; 57f60ac27eSMatt Spinler 58f60ac27eSMatt Spinler /** 59f60ac27eSMatt Spinler * @brief Constructor 60f60ac27eSMatt Spinler * 61f60ac27eSMatt Spinler * @param[in] repo - The PEL repository object 62f60ac27eSMatt Spinler * @param[in] dataIface - The data interface object 63f60ac27eSMatt Spinler * @param[in] hostIface - The host interface object 64f60ac27eSMatt Spinler */ 65f60ac27eSMatt Spinler HostNotifier(Repository& repo, DataInterfaceBase& dataIface, 66f60ac27eSMatt Spinler std::unique_ptr<HostInterface> hostIface); 67f60ac27eSMatt Spinler 68f60ac27eSMatt Spinler /** 69f60ac27eSMatt Spinler * @brief Destructor 70f60ac27eSMatt Spinler */ 71f60ac27eSMatt Spinler ~HostNotifier(); 72f60ac27eSMatt Spinler 73f60ac27eSMatt Spinler /** 74f60ac27eSMatt Spinler * @brief Returns the PEL queue size. 75f60ac27eSMatt Spinler * 76f60ac27eSMatt Spinler * For testing. 77f60ac27eSMatt Spinler * 78f60ac27eSMatt Spinler * @return size_t - The queue size 79f60ac27eSMatt Spinler */ queueSize() const80f60ac27eSMatt Spinler size_t queueSize() const 81f60ac27eSMatt Spinler { 82f60ac27eSMatt Spinler return _pelQueue.size(); 83f60ac27eSMatt Spinler } 84f60ac27eSMatt Spinler 85f60ac27eSMatt Spinler /** 86f60ac27eSMatt Spinler * @brief Specifies if the PEL needs to go onto the queue to be 87f60ac27eSMatt Spinler * set to the host. 88f60ac27eSMatt Spinler * 89a943b15bSMatt Spinler * Only returns false if: 90a943b15bSMatt Spinler * - Already acked by the host (or they didn't like it) 91a943b15bSMatt Spinler * - Hidden and the HMC already got it 92a943b15bSMatt Spinler * - The 'do not report to host' bit is set 93a943b15bSMatt Spinler * 94f60ac27eSMatt Spinler * @param[in] id - The PEL ID 95f60ac27eSMatt Spinler * 96f60ac27eSMatt Spinler * @return bool - If enqueue is required 97f60ac27eSMatt Spinler */ 98f60ac27eSMatt Spinler bool enqueueRequired(uint32_t id) const; 99f60ac27eSMatt Spinler 100f77debb9SMatt Spinler /** 101f77debb9SMatt Spinler * @brief If the host still needs to be notified of the PEL 102f77debb9SMatt Spinler * at the time of the notification. 103f77debb9SMatt Spinler * 104f77debb9SMatt Spinler * Only returns false if: 105f77debb9SMatt Spinler * - Already acked by the host 106f77debb9SMatt Spinler * - It's hidden, and the HMC already got or will get it. 107f77debb9SMatt Spinler * 108f77debb9SMatt Spinler * @param[in] id - The PEL ID 109f77debb9SMatt Spinler * 110f77debb9SMatt Spinler * @return bool - If the notify is required 111f77debb9SMatt Spinler */ 112f77debb9SMatt Spinler bool notifyRequired(uint32_t id) const; 113f77debb9SMatt Spinler 114cc3b64aeSMatt Spinler /** 115cc3b64aeSMatt Spinler * @brief Called when the host sends the 'ack' PLDM command. 116cc3b64aeSMatt Spinler * 117cc3b64aeSMatt Spinler * This means the PEL never needs to be sent up again. 118cc3b64aeSMatt Spinler * 11941293cb8SMatt Spinler * If the host was previously full, it is also an indication 12041293cb8SMatt Spinler * it no longer is. 12141293cb8SMatt Spinler * 122cc3b64aeSMatt Spinler * @param[in] id - The PEL ID 123cc3b64aeSMatt Spinler */ 124cc3b64aeSMatt Spinler void ackPEL(uint32_t id); 125cc3b64aeSMatt Spinler 12641293cb8SMatt Spinler /** 12741293cb8SMatt Spinler * @brief Called when the host does not have room for more 12841293cb8SMatt Spinler * PELs at this time. 12941293cb8SMatt Spinler * 13041293cb8SMatt Spinler * This can happen when an OS isn't running yet, and the 13141293cb8SMatt Spinler * staging area to hold the PELs before sending them up 13241293cb8SMatt Spinler * to the OS is full. This will stop future PEls from being 13341293cb8SMatt Spinler * sent up, as explained below. 13441293cb8SMatt Spinler * 13541293cb8SMatt Spinler * The PEL with this ID will need to be sent again, so its 13641293cb8SMatt Spinler * state is set back to 'new', and it is removed from the list 13741293cb8SMatt Spinler * of already sent PELs. 13841293cb8SMatt Spinler * 13941293cb8SMatt Spinler * A timer will be started, if it isn't already running, to 14041293cb8SMatt Spinler * issue another send in the hopes that space has been freed 14141293cb8SMatt Spinler * up by then (Receiving an ackPEL response is also an 14241293cb8SMatt Spinler * indication of this if there happened to have been other 14341293cb8SMatt Spinler * PELs in flight). 14441293cb8SMatt Spinler * 14541293cb8SMatt Spinler * @param[in] id - The PEL ID 14641293cb8SMatt Spinler */ 14741293cb8SMatt Spinler void setHostFull(uint32_t id); 14841293cb8SMatt Spinler 149a19b6234SMatt Spinler /** 150a19b6234SMatt Spinler * @brief Called when the host receives a malformed PEL. 151a19b6234SMatt Spinler * 152a19b6234SMatt Spinler * Ideally this will never happen, as the Repository 153a19b6234SMatt Spinler * class already purges malformed PELs. 154a19b6234SMatt Spinler * 155a19b6234SMatt Spinler * The PEL should never be sent up again. 156a19b6234SMatt Spinler * 157a19b6234SMatt Spinler * @param[in] id - The PEL ID 158a19b6234SMatt Spinler */ 159a19b6234SMatt Spinler void setBadPEL(uint32_t id); 160a19b6234SMatt Spinler 161f60ac27eSMatt Spinler private: 162f60ac27eSMatt Spinler /** 163f60ac27eSMatt Spinler * @brief This function gets called by the Repository class 164f60ac27eSMatt Spinler * when a new PEL is added to it. 165f60ac27eSMatt Spinler * 1667d800a4eSMatt Spinler * This function puts the PEL on the queue to be sent up if it 1677d800a4eSMatt Spinler * needs it, and possibly dispatch the send if the conditions call 1687d800a4eSMatt Spinler * for it. 1697d800a4eSMatt Spinler * 170f60ac27eSMatt Spinler * @param[in] pel - The new PEL 171f60ac27eSMatt Spinler */ 172f60ac27eSMatt Spinler void newLogCallback(const PEL& pel); 173f60ac27eSMatt Spinler 174f60ac27eSMatt Spinler /** 1757cb985ffSMatt Spinler * @brief This function gets called by the Repository class 1767cb985ffSMatt Spinler * when a PEL is deleted. 1777cb985ffSMatt Spinler * 1787cb985ffSMatt Spinler * The deleted ID will be removed from the PEL queue and the 1797cb985ffSMatt Spinler * sent list. 1807cb985ffSMatt Spinler * 1817cb985ffSMatt Spinler * @param[in] id - The deleted PEL ID 1827cb985ffSMatt Spinler */ 1837cb985ffSMatt Spinler void deleteLogCallback(uint32_t id); 1847cb985ffSMatt Spinler 1857cb985ffSMatt Spinler /** 186f60ac27eSMatt Spinler * @brief This function runs on every existing PEL at startup 187f60ac27eSMatt Spinler * and puts the PEL on the queue to send if necessary. 188f60ac27eSMatt Spinler * 189f60ac27eSMatt Spinler * @param[in] pel - The PEL 190f60ac27eSMatt Spinler * 191f60ac27eSMatt Spinler * @return bool - This is an indicator to the Repository::for_each 192f60ac27eSMatt Spinler * function to traverse every PEL. Always false. 193f60ac27eSMatt Spinler */ 194f60ac27eSMatt Spinler bool addPELToQueue(const PEL& pel); 195f60ac27eSMatt Spinler 196f60ac27eSMatt Spinler /** 197f77debb9SMatt Spinler * @brief Takes the first PEL from the queue that needs to be 198f77debb9SMatt Spinler * sent, and issues the send if conditions are right. 199f60ac27eSMatt Spinler */ 200f60ac27eSMatt Spinler void doNewLogNotify(); 201f60ac27eSMatt Spinler 202f60ac27eSMatt Spinler /** 2037d800a4eSMatt Spinler * @brief Creates the event object to handle sending the PLDM 2047d800a4eSMatt Spinler * command from the event loop. 2057d800a4eSMatt Spinler */ 2067d800a4eSMatt Spinler void scheduleDispatch(); 2077d800a4eSMatt Spinler 2087d800a4eSMatt Spinler /** 2097d800a4eSMatt Spinler * @brief Kicks off the PLDM send, but called from the event 2107d800a4eSMatt Spinler * loop. 2117d800a4eSMatt Spinler * 2127d800a4eSMatt Spinler * @param[in] source - The event source object 2137d800a4eSMatt Spinler */ 2147d800a4eSMatt Spinler void dispatch(sdeventplus::source::EventBase& source); 2157d800a4eSMatt Spinler 2167d800a4eSMatt Spinler /** 217f60ac27eSMatt Spinler * @brief Called when the host changes state. 218f60ac27eSMatt Spinler * 2193019c6fbSMatt Spinler * If the new state is host up and there are PELs to send, it 2203019c6fbSMatt Spinler * will trigger the first command. If the new state is off, then 2213019c6fbSMatt Spinler * it will transfer any PELs that were sent but not acked yet back 2223019c6fbSMatt Spinler * to the queue to be sent again. 2233019c6fbSMatt Spinler * 224f60ac27eSMatt Spinler * @param[in] hostUp - The new host state 225f60ac27eSMatt Spinler */ 226f60ac27eSMatt Spinler void hostStateChange(bool hostUp); 227f60ac27eSMatt Spinler 228f60ac27eSMatt Spinler /** 229f60ac27eSMatt Spinler * @brief The callback function invoked after the asynchronous 230f60ac27eSMatt Spinler * PLDM receive function is complete. 231f60ac27eSMatt Spinler * 232f869fcf8SMatt Spinler * If the command was successful, the state of that PEL will 233f869fcf8SMatt Spinler * be set to 'sent', and the next send will be triggered. 234f869fcf8SMatt Spinler * 235f869fcf8SMatt Spinler * If the command failed, a retry timer will be started so it 236f869fcf8SMatt Spinler * can be sent again. 237f869fcf8SMatt Spinler * 238f60ac27eSMatt Spinler * @param[in] status - The response status 239f60ac27eSMatt Spinler */ 240f60ac27eSMatt Spinler void commandResponse(ResponseStatus status); 241f60ac27eSMatt Spinler 242f60ac27eSMatt Spinler /** 243f869fcf8SMatt Spinler * @brief The function called when the command failure retry 244f869fcf8SMatt Spinler * time is up. 245f869fcf8SMatt Spinler * 246f869fcf8SMatt Spinler * It will issue a send of the previous PEL and increment the 247f869fcf8SMatt Spinler * retry count. 248f869fcf8SMatt Spinler */ 249f869fcf8SMatt Spinler void retryTimerExpired(); 250f869fcf8SMatt Spinler 251f869fcf8SMatt Spinler /** 25241293cb8SMatt Spinler * @brief The function called when the 'host full' retry timer 25341293cb8SMatt Spinler * expires. 25441293cb8SMatt Spinler * 25541293cb8SMatt Spinler * This will re-issue a command to try again with the PEL at 25641293cb8SMatt Spinler * the front of the queue. 25741293cb8SMatt Spinler */ 25841293cb8SMatt Spinler void hostFullTimerExpired(); 25941293cb8SMatt Spinler 26041293cb8SMatt Spinler /** 261e5f7508bSMatt Spinler * @brief The function called when the host up retry timer 262e5f7508bSMatt Spinler * expires. 263e5f7508bSMatt Spinler * 264e5f7508bSMatt Spinler * It kicks off sending the new log commands. 265e5f7508bSMatt Spinler */ 266e5f7508bSMatt Spinler void hostUpTimerExpired(); 267e5f7508bSMatt Spinler 268e5f7508bSMatt Spinler /** 2693019c6fbSMatt Spinler * @brief Stops an in progress command 2703019c6fbSMatt Spinler * 2713019c6fbSMatt Spinler * In progress meaning after the send but before the response. 2723019c6fbSMatt Spinler */ 2733019c6fbSMatt Spinler void stopCommand(); 2743019c6fbSMatt Spinler 2753019c6fbSMatt Spinler /** 276f60ac27eSMatt Spinler * @brief The PEL repository object 277f60ac27eSMatt Spinler */ 278f60ac27eSMatt Spinler Repository& _repo; 279f60ac27eSMatt Spinler 280f60ac27eSMatt Spinler /** 281f60ac27eSMatt Spinler * @brief The data interface object 282f60ac27eSMatt Spinler */ 283f60ac27eSMatt Spinler DataInterfaceBase& _dataIface; 284f60ac27eSMatt Spinler 285f60ac27eSMatt Spinler /** 286f60ac27eSMatt Spinler * @brief Base class pointer for the host command interface 287f60ac27eSMatt Spinler */ 288f60ac27eSMatt Spinler std::unique_ptr<HostInterface> _hostIface; 289f60ac27eSMatt Spinler 290f60ac27eSMatt Spinler /** 291f60ac27eSMatt Spinler * @brief The list of PEL IDs that need to be sent. 292f60ac27eSMatt Spinler */ 293f60ac27eSMatt Spinler std::deque<uint32_t> _pelQueue; 294f869fcf8SMatt Spinler 295f869fcf8SMatt Spinler /** 296f869fcf8SMatt Spinler * @brief The list of IDs that were sent, but not acked yet. 297f869fcf8SMatt Spinler * 298f869fcf8SMatt Spinler * These move back to _pelQueue on a power off. 299f869fcf8SMatt Spinler */ 300f869fcf8SMatt Spinler std::vector<uint32_t> _sentPELs; 301f869fcf8SMatt Spinler 302f869fcf8SMatt Spinler /** 303f869fcf8SMatt Spinler * @brief The ID the PEL where the notification has 304f869fcf8SMatt Spinler * been kicked off but the asynchronous response 305f869fcf8SMatt Spinler * hasn't been received yet. 306f869fcf8SMatt Spinler */ 307f869fcf8SMatt Spinler uint32_t _inProgressPEL = 0; 308f869fcf8SMatt Spinler 309f869fcf8SMatt Spinler /** 310f869fcf8SMatt Spinler * @brief The command retry count 311f869fcf8SMatt Spinler */ 312f869fcf8SMatt Spinler size_t _retryCount = 0; 313f869fcf8SMatt Spinler 314f869fcf8SMatt Spinler /** 31541293cb8SMatt Spinler * @brief Indicates if the host has said it is full and does not 31641293cb8SMatt Spinler * currently have the space for more PELs. 31741293cb8SMatt Spinler */ 31841293cb8SMatt Spinler bool _hostFull = false; 31941293cb8SMatt Spinler 32041293cb8SMatt Spinler /** 321f869fcf8SMatt Spinler * @brief The command retry timer. 322f869fcf8SMatt Spinler */ 323f869fcf8SMatt Spinler sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _retryTimer; 3247d800a4eSMatt Spinler 3257d800a4eSMatt Spinler /** 32641293cb8SMatt Spinler * @brief The host full timer, used to retry sending a PEL if the host 32741293cb8SMatt Spinler * said it is full. 32841293cb8SMatt Spinler */ 32941293cb8SMatt Spinler sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _hostFullTimer; 33041293cb8SMatt Spinler 33141293cb8SMatt Spinler /** 332e5f7508bSMatt Spinler * @brief The host up timer, used to kick off sending commands to the 333e5f7508bSMatt Spinler * host after a delay after the host is up. 334e5f7508bSMatt Spinler */ 335e5f7508bSMatt Spinler sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _hostUpTimer; 336e5f7508bSMatt Spinler 337e5f7508bSMatt Spinler /** 3387d800a4eSMatt Spinler * @brief The object used to dispatch a new PEL send from the 3397d800a4eSMatt Spinler * event loop, so the calling function can be returned from 3407d800a4eSMatt Spinler * first. 3417d800a4eSMatt Spinler */ 3427d800a4eSMatt Spinler std::unique_ptr<sdeventplus::source::Defer> _dispatcher; 343f60ac27eSMatt Spinler }; 344f60ac27eSMatt Spinler 345f60ac27eSMatt Spinler } // namespace openpower::pels 346