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