blob: 24c3f6f4b1cc8d5aef907f880e1f976b4a98850d [file] [log] [blame]
Matt Spinlerf60ac272019-12-11 13:47:50 -06001#pragma once
2
3#include "host_interface.hpp"
4#include "pel.hpp"
5#include "repository.hpp"
6
7#include <deque>
Matt Spinlerf869fcf2019-12-11 15:02:20 -06008#include <sdeventplus/clock.hpp>
Matt Spinler7d800a42019-12-12 10:35:01 -06009#include <sdeventplus/source/event.hpp>
Matt Spinlerf869fcf2019-12-11 15:02:20 -060010#include <sdeventplus/utility/timer.hpp>
Matt Spinlerf60ac272019-12-11 13:47:50 -060011
12namespace openpower::pels
13{
14
15/**
16 * @class HostNotifier
17 *
18 * This class handles notifying the host firmware of new PELs.
Matt Spinler0e1593e2019-12-16 10:50:43 -060019 *
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.
Matt Spinlerf60ac272019-12-11 13:47:50 -060047 */
48class 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 *
Matt Spinlera943b152019-12-11 14:44:50 -060088 * 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 *
Matt Spinlerf60ac272019-12-11 13:47:50 -060093 * @param[in] id - The PEL ID
94 *
95 * @return bool - If enqueue is required
96 */
97 bool enqueueRequired(uint32_t id) const;
98
Matt Spinlerf77debb2019-12-12 10:04:33 -060099 /**
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
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600113 /**
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 *
Matt Spinler41293cb2019-12-12 13:11:09 -0600118 * If the host was previously full, it is also an indication
119 * it no longer is.
120 *
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600121 * @param[in] id - The PEL ID
122 */
123 void ackPEL(uint32_t id);
124
Matt Spinler41293cb2019-12-12 13:11:09 -0600125 /**
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
Matt Spinlera19b6232019-12-12 13:30:14 -0600148 /**
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
Matt Spinlerf60ac272019-12-11 13:47:50 -0600160 private:
161 /**
162 * @brief This function gets called by the Repository class
163 * when a new PEL is added to it.
164 *
Matt Spinler7d800a42019-12-12 10:35:01 -0600165 * 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 *
Matt Spinlerf60ac272019-12-11 13:47:50 -0600169 * @param[in] pel - The new PEL
170 */
171 void newLogCallback(const PEL& pel);
172
173 /**
Matt Spinler7cb985f2020-03-05 16:02:39 -0600174 * @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 /**
Matt Spinlerf60ac272019-12-11 13:47:50 -0600185 * @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 /**
Matt Spinlerf77debb2019-12-12 10:04:33 -0600196 * @brief Takes the first PEL from the queue that needs to be
197 * sent, and issues the send if conditions are right.
Matt Spinlerf60ac272019-12-11 13:47:50 -0600198 */
199 void doNewLogNotify();
200
201 /**
Matt Spinler7d800a42019-12-12 10:35:01 -0600202 * @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 /**
Matt Spinlerf60ac272019-12-11 13:47:50 -0600216 * @brief Called when the host changes state.
217 *
Matt Spinler3019c6f2019-12-11 15:24:45 -0600218 * 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 *
Matt Spinlerf60ac272019-12-11 13:47:50 -0600223 * @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 *
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600231 * 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 *
Matt Spinlerf60ac272019-12-11 13:47:50 -0600237 * @param[in] status - The response status
238 */
239 void commandResponse(ResponseStatus status);
240
241 /**
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600242 * @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 /**
Matt Spinler41293cb2019-12-12 13:11:09 -0600251 * @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 /**
Matt Spinler3019c6f2019-12-11 15:24:45 -0600260 * @brief Stops an in progress command
261 *
262 * In progress meaning after the send but before the response.
263 */
264 void stopCommand();
265
266 /**
Matt Spinlerf60ac272019-12-11 13:47:50 -0600267 * @brief The PEL repository object
268 */
269 Repository& _repo;
270
271 /**
272 * @brief The data interface object
273 */
274 DataInterfaceBase& _dataIface;
275
276 /**
277 * @brief Base class pointer for the host command interface
278 */
279 std::unique_ptr<HostInterface> _hostIface;
280
281 /**
282 * @brief The list of PEL IDs that need to be sent.
283 */
284 std::deque<uint32_t> _pelQueue;
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600285
286 /**
287 * @brief The list of IDs that were sent, but not acked yet.
288 *
289 * These move back to _pelQueue on a power off.
290 */
291 std::vector<uint32_t> _sentPELs;
292
293 /**
294 * @brief The ID the PEL where the notification has
295 * been kicked off but the asynchronous response
296 * hasn't been received yet.
297 */
298 uint32_t _inProgressPEL = 0;
299
300 /**
301 * @brief The command retry count
302 */
303 size_t _retryCount = 0;
304
305 /**
Matt Spinler41293cb2019-12-12 13:11:09 -0600306 * @brief Indicates if the host has said it is full and does not
307 * currently have the space for more PELs.
308 */
309 bool _hostFull = false;
310
311 /**
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600312 * @brief The command retry timer.
313 */
314 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _retryTimer;
Matt Spinler7d800a42019-12-12 10:35:01 -0600315
316 /**
Matt Spinler41293cb2019-12-12 13:11:09 -0600317 * @brief The host full timer, used to retry sending a PEL if the host
318 * said it is full.
319 */
320 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _hostFullTimer;
321
322 /**
Matt Spinler7d800a42019-12-12 10:35:01 -0600323 * @brief The object used to dispatch a new PEL send from the
324 * event loop, so the calling function can be returned from
325 * first.
326 */
327 std::unique_ptr<sdeventplus::source::Defer> _dispatcher;
Matt Spinlerf60ac272019-12-11 13:47:50 -0600328};
329
330} // namespace openpower::pels