blob: cad2b367695947061214102ddc98ecf27e8f0c54 [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 /**
174 * @brief This function runs on every existing PEL at startup
175 * and puts the PEL on the queue to send if necessary.
176 *
177 * @param[in] pel - The PEL
178 *
179 * @return bool - This is an indicator to the Repository::for_each
180 * function to traverse every PEL. Always false.
181 */
182 bool addPELToQueue(const PEL& pel);
183
184 /**
Matt Spinlerf77debb2019-12-12 10:04:33 -0600185 * @brief Takes the first PEL from the queue that needs to be
186 * sent, and issues the send if conditions are right.
Matt Spinlerf60ac272019-12-11 13:47:50 -0600187 */
188 void doNewLogNotify();
189
190 /**
Matt Spinler7d800a42019-12-12 10:35:01 -0600191 * @brief Creates the event object to handle sending the PLDM
192 * command from the event loop.
193 */
194 void scheduleDispatch();
195
196 /**
197 * @brief Kicks off the PLDM send, but called from the event
198 * loop.
199 *
200 * @param[in] source - The event source object
201 */
202 void dispatch(sdeventplus::source::EventBase& source);
203
204 /**
Matt Spinlerf60ac272019-12-11 13:47:50 -0600205 * @brief Called when the host changes state.
206 *
Matt Spinler3019c6f2019-12-11 15:24:45 -0600207 * If the new state is host up and there are PELs to send, it
208 * will trigger the first command. If the new state is off, then
209 * it will transfer any PELs that were sent but not acked yet back
210 * to the queue to be sent again.
211 *
Matt Spinlerf60ac272019-12-11 13:47:50 -0600212 * @param[in] hostUp - The new host state
213 */
214 void hostStateChange(bool hostUp);
215
216 /**
217 * @brief The callback function invoked after the asynchronous
218 * PLDM receive function is complete.
219 *
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600220 * If the command was successful, the state of that PEL will
221 * be set to 'sent', and the next send will be triggered.
222 *
223 * If the command failed, a retry timer will be started so it
224 * can be sent again.
225 *
Matt Spinlerf60ac272019-12-11 13:47:50 -0600226 * @param[in] status - The response status
227 */
228 void commandResponse(ResponseStatus status);
229
230 /**
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600231 * @brief The function called when the command failure retry
232 * time is up.
233 *
234 * It will issue a send of the previous PEL and increment the
235 * retry count.
236 */
237 void retryTimerExpired();
238
239 /**
Matt Spinler41293cb2019-12-12 13:11:09 -0600240 * @brief The function called when the 'host full' retry timer
241 * expires.
242 *
243 * This will re-issue a command to try again with the PEL at
244 * the front of the queue.
245 */
246 void hostFullTimerExpired();
247
248 /**
Matt Spinler3019c6f2019-12-11 15:24:45 -0600249 * @brief Stops an in progress command
250 *
251 * In progress meaning after the send but before the response.
252 */
253 void stopCommand();
254
255 /**
Matt Spinlerf60ac272019-12-11 13:47:50 -0600256 * @brief The PEL repository object
257 */
258 Repository& _repo;
259
260 /**
261 * @brief The data interface object
262 */
263 DataInterfaceBase& _dataIface;
264
265 /**
266 * @brief Base class pointer for the host command interface
267 */
268 std::unique_ptr<HostInterface> _hostIface;
269
270 /**
271 * @brief The list of PEL IDs that need to be sent.
272 */
273 std::deque<uint32_t> _pelQueue;
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600274
275 /**
276 * @brief The list of IDs that were sent, but not acked yet.
277 *
278 * These move back to _pelQueue on a power off.
279 */
280 std::vector<uint32_t> _sentPELs;
281
282 /**
283 * @brief The ID the PEL where the notification has
284 * been kicked off but the asynchronous response
285 * hasn't been received yet.
286 */
287 uint32_t _inProgressPEL = 0;
288
289 /**
290 * @brief The command retry count
291 */
292 size_t _retryCount = 0;
293
294 /**
Matt Spinler41293cb2019-12-12 13:11:09 -0600295 * @brief Indicates if the host has said it is full and does not
296 * currently have the space for more PELs.
297 */
298 bool _hostFull = false;
299
300 /**
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600301 * @brief The command retry timer.
302 */
303 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _retryTimer;
Matt Spinler7d800a42019-12-12 10:35:01 -0600304
305 /**
Matt Spinler41293cb2019-12-12 13:11:09 -0600306 * @brief The host full timer, used to retry sending a PEL if the host
307 * said it is full.
308 */
309 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _hostFullTimer;
310
311 /**
Matt Spinler7d800a42019-12-12 10:35:01 -0600312 * @brief The object used to dispatch a new PEL send from the
313 * event loop, so the calling function can be returned from
314 * first.
315 */
316 std::unique_ptr<sdeventplus::source::Defer> _dispatcher;
Matt Spinlerf60ac272019-12-11 13:47:50 -0600317};
318
319} // namespace openpower::pels