1 #pragma once 2 3 #include "host_interface.hpp" 4 #include "pel.hpp" 5 #include "repository.hpp" 6 7 #include <sdeventplus/clock.hpp> 8 #include <sdeventplus/source/event.hpp> 9 #include <sdeventplus/utility/timer.hpp> 10 11 #include <deque> 12 13 namespace openpower::pels 14 { 15 16 /** 17 * @class HostNotifier 18 * 19 * This class handles notifying the host firmware of new PELs. 20 * 21 * It uses the Repository class's subscription feature to be 22 * notified about new PELs. 23 * 24 * Some PELs do not need to be sent - see enqueueRequired() and 25 * notifyRequired(). 26 * 27 * The high level good path flow for sending a single PEL is: 28 * 29 * 1) Send the ID and size of the new PEL to the host. 30 * - The command response is asynchronous. 31 * 32 * 2) The host reads the raw PEL data (outside of this class). 33 * 34 * 3) The host sends the PEL to the OS, and then sends an AckPEL 35 * PLDM command to the PLDM daemon, who makes a D-Bus method 36 * call to this daemon, which calls HostNotifer::ackPEL(). 37 * 38 * After this, a PEL never needs to be sent again, but if the 39 * host is rebooted before the ack comes it will. 40 * 41 * The host firmware has a finite amount of space to store PELs before 42 * sending to the OS, and it's possible it will fill up. In this case, 43 * the AckPEL command will have a special response that will tell the 44 * PLDM daemon to call HostReject D-Bus method on this daemon instead 45 * which will invoke HostNotifier::setHostFull(). This will stop new 46 * PELs from being sent, and the first PEL that hits this will have 47 * a timer set to retry again later. 48 */ 49 class HostNotifier 50 { 51 public: 52 HostNotifier() = delete; 53 HostNotifier(const HostNotifier&) = delete; 54 HostNotifier& operator=(const HostNotifier&) = delete; 55 HostNotifier(HostNotifier&&) = delete; 56 HostNotifier& operator=(HostNotifier&&) = delete; 57 58 /** 59 * @brief Constructor 60 * 61 * @param[in] repo - The PEL repository object 62 * @param[in] dataIface - The data interface object 63 * @param[in] hostIface - The host interface object 64 */ 65 HostNotifier(Repository& repo, DataInterfaceBase& dataIface, 66 std::unique_ptr<HostInterface> hostIface); 67 68 /** 69 * @brief Destructor 70 */ 71 ~HostNotifier(); 72 73 /** 74 * @brief Returns the PEL queue size. 75 * 76 * For testing. 77 * 78 * @return size_t - The queue size 79 */ 80 size_t queueSize() const 81 { 82 return _pelQueue.size(); 83 } 84 85 /** 86 * @brief Specifies if the PEL needs to go onto the queue to be 87 * set to the host. 88 * 89 * Only returns false if: 90 * - Already acked by the host (or they didn't like it) 91 * - Hidden and the HMC already got it 92 * - The 'do not report to host' bit is set 93 * 94 * @param[in] id - The PEL ID 95 * 96 * @return bool - If enqueue is required 97 */ 98 bool enqueueRequired(uint32_t id) const; 99 100 /** 101 * @brief If the host still needs to be notified of the PEL 102 * at the time of the notification. 103 * 104 * Only returns false if: 105 * - Already acked by the host 106 * - It's hidden, and the HMC already got or will get it. 107 * 108 * @param[in] id - The PEL ID 109 * 110 * @return bool - If the notify is required 111 */ 112 bool notifyRequired(uint32_t id) const; 113 114 /** 115 * @brief Called when the host sends the 'ack' PLDM command. 116 * 117 * This means the PEL never needs to be sent up again. 118 * 119 * If the host was previously full, it is also an indication 120 * it no longer is. 121 * 122 * @param[in] id - The PEL ID 123 */ 124 void ackPEL(uint32_t id); 125 126 /** 127 * @brief Called when the host does not have room for more 128 * PELs at this time. 129 * 130 * This can happen when an OS isn't running yet, and the 131 * staging area to hold the PELs before sending them up 132 * to the OS is full. This will stop future PEls from being 133 * sent up, as explained below. 134 * 135 * The PEL with this ID will need to be sent again, so its 136 * state is set back to 'new', and it is removed from the list 137 * of already sent PELs. 138 * 139 * A timer will be started, if it isn't already running, to 140 * issue another send in the hopes that space has been freed 141 * up by then (Receiving an ackPEL response is also an 142 * indication of this if there happened to have been other 143 * PELs in flight). 144 * 145 * @param[in] id - The PEL ID 146 */ 147 void setHostFull(uint32_t id); 148 149 /** 150 * @brief Called when the host receives a malformed PEL. 151 * 152 * Ideally this will never happen, as the Repository 153 * class already purges malformed PELs. 154 * 155 * The PEL should never be sent up again. 156 * 157 * @param[in] id - The PEL ID 158 */ 159 void setBadPEL(uint32_t id); 160 161 private: 162 /** 163 * @brief This function gets called by the Repository class 164 * when a new PEL is added to it. 165 * 166 * This function puts the PEL on the queue to be sent up if it 167 * needs it, and possibly dispatch the send if the conditions call 168 * for it. 169 * 170 * @param[in] pel - The new PEL 171 */ 172 void newLogCallback(const PEL& pel); 173 174 /** 175 * @brief This function gets called by the Repository class 176 * when a PEL is deleted. 177 * 178 * The deleted ID will be removed from the PEL queue and the 179 * sent list. 180 * 181 * @param[in] id - The deleted PEL ID 182 */ 183 void deleteLogCallback(uint32_t id); 184 185 /** 186 * @brief This function runs on every existing PEL at startup 187 * and puts the PEL on the queue to send if necessary. 188 * 189 * @param[in] pel - The PEL 190 * 191 * @return bool - This is an indicator to the Repository::for_each 192 * function to traverse every PEL. Always false. 193 */ 194 bool addPELToQueue(const PEL& pel); 195 196 /** 197 * @brief Takes the first PEL from the queue that needs to be 198 * sent, and issues the send if conditions are right. 199 */ 200 void doNewLogNotify(); 201 202 /** 203 * @brief Creates the event object to handle sending the PLDM 204 * command from the event loop. 205 */ 206 void scheduleDispatch(); 207 208 /** 209 * @brief Kicks off the PLDM send, but called from the event 210 * loop. 211 * 212 * @param[in] source - The event source object 213 */ 214 void dispatch(sdeventplus::source::EventBase& source); 215 216 /** 217 * @brief Called when the host changes state. 218 * 219 * If the new state is host up and there are PELs to send, it 220 * will trigger the first command. If the new state is off, then 221 * it will transfer any PELs that were sent but not acked yet back 222 * to the queue to be sent again. 223 * 224 * @param[in] hostUp - The new host state 225 */ 226 void hostStateChange(bool hostUp); 227 228 /** 229 * @brief The callback function invoked after the asynchronous 230 * PLDM receive function is complete. 231 * 232 * If the command was successful, the state of that PEL will 233 * be set to 'sent', and the next send will be triggered. 234 * 235 * If the command failed, a retry timer will be started so it 236 * can be sent again. 237 * 238 * @param[in] status - The response status 239 */ 240 void commandResponse(ResponseStatus status); 241 242 /** 243 * @brief The function called when the command failure retry 244 * time is up. 245 * 246 * It will issue a send of the previous PEL and increment the 247 * retry count. 248 */ 249 void retryTimerExpired(); 250 251 /** 252 * @brief The function called when the 'host full' retry timer 253 * expires. 254 * 255 * This will re-issue a command to try again with the PEL at 256 * the front of the queue. 257 */ 258 void hostFullTimerExpired(); 259 260 /** 261 * @brief The function called when the host up retry timer 262 * expires. 263 * 264 * It kicks off sending the new log commands. 265 */ 266 void hostUpTimerExpired(); 267 268 /** 269 * @brief Stops an in progress command 270 * 271 * In progress meaning after the send but before the response. 272 */ 273 void stopCommand(); 274 275 /** 276 * @brief The PEL repository object 277 */ 278 Repository& _repo; 279 280 /** 281 * @brief The data interface object 282 */ 283 DataInterfaceBase& _dataIface; 284 285 /** 286 * @brief Base class pointer for the host command interface 287 */ 288 std::unique_ptr<HostInterface> _hostIface; 289 290 /** 291 * @brief The list of PEL IDs that need to be sent. 292 */ 293 std::deque<uint32_t> _pelQueue; 294 295 /** 296 * @brief The list of IDs that were sent, but not acked yet. 297 * 298 * These move back to _pelQueue on a power off. 299 */ 300 std::vector<uint32_t> _sentPELs; 301 302 /** 303 * @brief The ID the PEL where the notification has 304 * been kicked off but the asynchronous response 305 * hasn't been received yet. 306 */ 307 uint32_t _inProgressPEL = 0; 308 309 /** 310 * @brief The command retry count 311 */ 312 size_t _retryCount = 0; 313 314 /** 315 * @brief Indicates if the host has said it is full and does not 316 * currently have the space for more PELs. 317 */ 318 bool _hostFull = false; 319 320 /** 321 * @brief The command retry timer. 322 */ 323 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _retryTimer; 324 325 /** 326 * @brief The host full timer, used to retry sending a PEL if the host 327 * said it is full. 328 */ 329 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _hostFullTimer; 330 331 /** 332 * @brief The host up timer, used to kick off sending commands to the 333 * host after a delay after the host is up. 334 */ 335 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _hostUpTimer; 336 337 /** 338 * @brief The object used to dispatch a new PEL send from the 339 * event loop, so the calling function can be returned from 340 * first. 341 */ 342 std::unique_ptr<sdeventplus::source::Defer> _dispatcher; 343 }; 344 345 } // namespace openpower::pels 346