blob: 33234dc0550c12dd1940f1fb82b9a3da4806ab27 [file] [log] [blame]
Matt Spinlerf60ac272019-12-11 13:47:50 -06001/**
2 * Copyright © 2019 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include "extensions/openpower-pels/data_interface.hpp"
17#include "extensions/openpower-pels/host_notifier.hpp"
18#include "mocks.hpp"
19#include "pel_utils.hpp"
20
21#include <fcntl.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24
25#include <chrono>
26
27#include <gtest/gtest.h>
28
29using namespace openpower::pels;
30using ::testing::_;
31using ::testing::Invoke;
Matt Spinler56e08262020-01-27 16:14:23 -060032using ::testing::NiceMock;
Matt Spinlerf60ac272019-12-11 13:47:50 -060033using ::testing::Return;
34namespace fs = std::filesystem;
35using namespace std::chrono;
36
37const size_t actionFlags0Offset = 66;
38const size_t actionFlags1Offset = 67;
39
40class HostNotifierTest : public CleanPELFiles
41{
Matt Spinlera943b152019-12-11 14:44:50 -060042 public:
Matt Spinler56e08262020-01-27 16:14:23 -060043 HostNotifierTest() : repo(repoPath)
Matt Spinlera943b152019-12-11 14:44:50 -060044 {
45 auto r = sd_event_default(&event);
46 EXPECT_TRUE(r >= 0);
Matt Spinler56e08262020-01-27 16:14:23 -060047
Matt Spinler24a85582020-01-27 16:40:21 -060048 ON_CALL(dataIface, getHostPELEnablement).WillByDefault(Return(true));
49
Patrick Williams075c7922024-08-16 15:19:49 -040050 hostIface =
51 std::make_unique<NiceMock<MockHostInterface>>(event, dataIface);
Matt Spinler56e08262020-01-27 16:14:23 -060052
53 mockHostIface = reinterpret_cast<MockHostInterface*>(hostIface.get());
54
Patrick Williamsd26fa3e2021-04-21 15:22:23 -050055 auto send = [this](uint32_t /*id*/, uint32_t /*size*/) {
Matt Spinler56e08262020-01-27 16:14:23 -060056 return this->mockHostIface->send(0);
57 };
58
59 // Unless otherwise specified, sendNewLogCmd should always pass.
Matt Spinlerd438c092025-10-24 10:00:02 -050060 ON_CALL(*mockHostIface, sendNewLogCmd(_, _)).WillByDefault(send);
Matt Spinlera943b152019-12-11 14:44:50 -060061 }
62
63 ~HostNotifierTest()
64 {
65 sd_event_unref(event);
66 }
67
68 protected:
69 sd_event* event;
Matt Spinler56e08262020-01-27 16:14:23 -060070 Repository repo;
71 NiceMock<MockDataInterface> dataIface;
72 std::unique_ptr<HostInterface> hostIface;
73 MockHostInterface* mockHostIface;
Matt Spinlerf60ac272019-12-11 13:47:50 -060074};
75
76/**
77 * @brief Create PEL with the specified action flags
78 *
79 * @param[in] actionFlagsMask - Optional action flags to use
80 *
81 * @return std::unique_ptr<PEL>
82 */
83std::unique_ptr<PEL> makePEL(uint16_t actionFlagsMask = 0)
84{
85 static uint32_t obmcID = 1;
86 auto data = pelDataFactory(TestPELType::pelSimple);
87
88 data[actionFlags0Offset] |= actionFlagsMask >> 8;
89 data[actionFlags1Offset] |= actionFlagsMask & 0xFF;
90
91 auto pel = std::make_unique<PEL>(data, obmcID++);
92 pel->assignID();
93 pel->setCommitTime();
94 return pel;
95}
96
Matt Spinler7d800a42019-12-12 10:35:01 -060097/**
98 * @brief Run an iteration of the event loop.
99 *
100 * An event loop is used for:
101 * 1) timer expiration callbacks
102 * 2) Dispatches
103 * 3) host interface receive callbacks
104 *
105 * @param[in] event - The event object
106 * @param[in] numEvents - number of times to call Event::run()
107 * @param[in] timeout - timeout value for run()
108 */
109void runEvents(sdeventplus::Event& event, size_t numEvents,
110 milliseconds timeout = milliseconds(1))
111{
112 for (size_t i = 0; i < numEvents; i++)
113 {
114 event.run(timeout);
115 }
116}
117
Matt Spinlerf60ac272019-12-11 13:47:50 -0600118// Test that host state change callbacks work
119TEST_F(HostNotifierTest, TestHostStateChange)
120{
Matt Spinlerf60ac272019-12-11 13:47:50 -0600121 bool hostState = false;
122 bool called = false;
Patrick Williams075c7922024-08-16 15:19:49 -0400123 DataInterfaceBase::HostStateChangeFunc func =
124 [&hostState, &called](bool state) {
125 hostState = state;
126 called = true;
127 };
Matt Spinlerf60ac272019-12-11 13:47:50 -0600128
129 dataIface.subscribeToHostStateChange("test", func);
130
131 // callback called
132 dataIface.changeHostState(true);
133 EXPECT_TRUE(called);
134 EXPECT_TRUE(hostState);
135
136 // No change, not called
137 called = false;
138 dataIface.changeHostState(true);
139 EXPECT_FALSE(called);
140
141 // Called again
142 dataIface.changeHostState(false);
143 EXPECT_FALSE(hostState);
144 EXPECT_TRUE(called);
145
146 // Shouldn't get called after an unsubscribe
147 dataIface.unsubscribeFromHostStateChange("test");
148
149 called = false;
150
151 dataIface.changeHostState(true);
152 EXPECT_FALSE(called);
153}
154
Matt Spinlera943b152019-12-11 14:44:50 -0600155// Test dealing with how acked PELs are put on the
156// notification queue.
157TEST_F(HostNotifierTest, TestPolicyAckedPEL)
158{
Matt Spinlera943b152019-12-11 14:44:50 -0600159 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
160
161 auto pel = makePEL();
162 repo.add(pel);
163
164 // This is required
165 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600166 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600167
168 // Not in the repo
169 EXPECT_FALSE(notifier.enqueueRequired(42));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600170 EXPECT_FALSE(notifier.notifyRequired(42));
Matt Spinlera943b152019-12-11 14:44:50 -0600171
172 // Now set this PEL to host acked
173 repo.setPELHostTransState(pel->id(), TransmissionState::acked);
174
175 // Since it's acked, doesn't need to be enqueued or transmitted
176 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600177 EXPECT_FALSE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600178}
179
180// Test the 'don't report' PEL flag
181TEST_F(HostNotifierTest, TestPolicyDontReport)
182{
Matt Spinlera943b152019-12-11 14:44:50 -0600183 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
184
185 // dontReportToHostFlagBit
186 auto pel = makePEL(0x1000);
187
188 // Double check the action flag is still set
189 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
190 EXPECT_TRUE(actionFlags.test(dontReportToHostFlagBit));
191
192 repo.add(pel);
193
194 // Don't need to send this to the host
195 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
196}
197
198// Test that hidden PELs need notification when there
199// is no HMC.
200TEST_F(HostNotifierTest, TestPolicyHiddenNoHMC)
201{
Matt Spinlera943b152019-12-11 14:44:50 -0600202 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
203
204 // hiddenFlagBit
205 auto pel = makePEL(0x4000);
206
207 // Double check the action flag is still set
208 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
209 EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
210
211 repo.add(pel);
212
213 // Still need to enqueue this
214 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600215
216 // Still need to send it
217 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600218}
219
220// Don't need to enqueue a hidden log already acked by the HMC
221TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCAcked)
222{
Matt Spinlera943b152019-12-11 14:44:50 -0600223 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
224
225 // hiddenFlagBit
226 auto pel = makePEL(0x4000);
227
228 // Double check the action flag is still set
229 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
230 EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
231
232 repo.add(pel);
233
234 // No HMC yet, so required
235 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
236
237 repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
238
239 // Not required anymore
240 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
241}
242
Matt Spinlerf77debb2019-12-12 10:04:33 -0600243// Test that changing the HMC manage status affects
244// the policy with hidden log notification.
245TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCManaged)
246{
Matt Spinlerf77debb2019-12-12 10:04:33 -0600247 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
248
249 // hiddenFlagBit
250 auto pel = makePEL(0x4000);
251
252 repo.add(pel);
253
254 // The first time, the HMC managed is false
255 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
256
257 dataIface.setHMCManaged(true);
258
259 // This time, HMC managed is true so no need to notify
260 EXPECT_FALSE(notifier.notifyRequired(pel->id()));
261}
262
Matt Spinlerf60ac272019-12-11 13:47:50 -0600263// Test that PELs are enqueued on startup
264TEST_F(HostNotifierTest, TestStartup)
265{
Matt Spinlerf60ac272019-12-11 13:47:50 -0600266 // Give the repo 10 PELs to start with
267 for (int i = 0; i < 10; i++)
268 {
269 auto pel = makePEL();
270 repo.add(pel);
271 }
272
Matt Spinlerf60ac272019-12-11 13:47:50 -0600273 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
274
275 ASSERT_EQ(notifier.queueSize(), 10);
276
277 // Now add 10 more after the notifier is watching
278 for (int i = 0; i < 10; i++)
279 {
280 auto pel = makePEL();
281 repo.add(pel);
282 }
283
284 ASSERT_EQ(notifier.queueSize(), 20);
285}
Matt Spinler7d800a42019-12-12 10:35:01 -0600286
287// Test the simple path were PELs get sent to the host
288TEST_F(HostNotifierTest, TestSendCmd)
289{
Matt Spinler7d800a42019-12-12 10:35:01 -0600290 sdeventplus::Event sdEvent{event};
291
Matt Spinler7d800a42019-12-12 10:35:01 -0600292 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
293
Matt Spinler7d800a42019-12-12 10:35:01 -0600294 // Add a PEL with the host off
295 auto pel = makePEL();
296 repo.add(pel);
297
298 EXPECT_EQ(notifier.queueSize(), 1);
299
300 dataIface.changeHostState(true);
301
Matt Spinlere5f75082022-01-24 16:09:51 -0600302 runEvents(sdEvent, 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600303
304 // It was sent up
Matt Spinler56e08262020-01-27 16:14:23 -0600305 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600306 EXPECT_EQ(notifier.queueSize(), 0);
307
308 // Verify the state was written to the PEL.
309 Repository::LogID id{Repository::LogID::Pel{pel->id()}};
310 auto data = repo.getPELData(id);
311 PEL pelFromRepo{*data};
312 EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::sent);
313
314 // Add a few more PELs. They will get sent.
315 pel = makePEL();
316 repo.add(pel);
317
318 // Dispatch it by hitting the event loop (no commands sent yet)
319 // Don't need to test this step discretely in the future
320 runEvents(sdEvent, 1);
Matt Spinler56e08262020-01-27 16:14:23 -0600321 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600322 EXPECT_EQ(notifier.queueSize(), 0);
323
324 // Send the command
325 runEvents(sdEvent, 1);
326
Matt Spinler56e08262020-01-27 16:14:23 -0600327 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600328 EXPECT_EQ(notifier.queueSize(), 0);
329
330 pel = makePEL();
331 repo.add(pel);
332
333 // dispatch and process the command
334 runEvents(sdEvent, 2);
335
Matt Spinler56e08262020-01-27 16:14:23 -0600336 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler7d800a42019-12-12 10:35:01 -0600337 EXPECT_EQ(notifier.queueSize(), 0);
338}
339
340// Test that if the class is created with the host up,
341// it will send PELs
342TEST_F(HostNotifierTest, TestStartAfterHostUp)
343{
Matt Spinler7d800a42019-12-12 10:35:01 -0600344 // Add PELs right away
345 auto pel = makePEL();
346 repo.add(pel);
347 pel = makePEL();
348 repo.add(pel);
349
350 sdeventplus::Event sdEvent{event};
351
Matt Spinler7d800a42019-12-12 10:35:01 -0600352 // Create the HostNotifier class with the host already up
353 dataIface.changeHostState(true);
354 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
355
356 // It should start sending PELs right away
Matt Spinlere5f75082022-01-24 16:09:51 -0600357 runEvents(sdEvent, 3);
Matt Spinler7d800a42019-12-12 10:35:01 -0600358
Matt Spinler56e08262020-01-27 16:14:23 -0600359 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600360 EXPECT_EQ(notifier.queueSize(), 0);
361}
362
363// Test that a single failure will cause a retry
364TEST_F(HostNotifierTest, TestHostRetry)
365{
Matt Spinler7d800a42019-12-12 10:35:01 -0600366 sdeventplus::Event sdEvent{event};
367
Matt Spinler7d800a42019-12-12 10:35:01 -0600368 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
369
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500370 auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
Matt Spinler56e08262020-01-27 16:14:23 -0600371 return this->mockHostIface->send(1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600372 };
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500373 auto sendSuccess = [this](uint32_t /*id*/, uint32_t /*size*/) {
Matt Spinler56e08262020-01-27 16:14:23 -0600374 return this->mockHostIface->send(0);
Matt Spinler7d800a42019-12-12 10:35:01 -0600375 };
376
Matt Spinler56e08262020-01-27 16:14:23 -0600377 EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
Matt Spinlerd438c092025-10-24 10:00:02 -0500378 .WillOnce(sendFailure)
379 .WillOnce(sendSuccess)
380 .WillOnce(sendSuccess);
Matt Spinler7d800a42019-12-12 10:35:01 -0600381
382 dataIface.changeHostState(true);
383
384 auto pel = makePEL();
385 repo.add(pel);
386
387 // Dispatch and handle the command
388 runEvents(sdEvent, 2);
389
390 // The command failed, so the queue isn't empty
Matt Spinler56e08262020-01-27 16:14:23 -0600391 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600392 EXPECT_EQ(notifier.queueSize(), 1);
393
394 // Run the events again to let the timer expire and the
395 // command to be retried, which will be successful.
Matt Spinler56e08262020-01-27 16:14:23 -0600396 runEvents(sdEvent, 2, mockHostIface->getReceiveRetryDelay());
Matt Spinler7d800a42019-12-12 10:35:01 -0600397
Matt Spinler56e08262020-01-27 16:14:23 -0600398 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600399 EXPECT_EQ(notifier.queueSize(), 0);
400
401 // This one should pass with no problems
402 pel = makePEL();
403 repo.add(pel);
404
405 // Dispatch and handle the command
406 runEvents(sdEvent, 2);
407
Matt Spinler56e08262020-01-27 16:14:23 -0600408 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler7d800a42019-12-12 10:35:01 -0600409 EXPECT_EQ(notifier.queueSize(), 0);
410}
411
412// Test that all commands fail and notifier will give up
413TEST_F(HostNotifierTest, TestHardFailure)
414{
Matt Spinler7d800a42019-12-12 10:35:01 -0600415 sdeventplus::Event sdEvent{event};
416
Matt Spinler7d800a42019-12-12 10:35:01 -0600417 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
418
419 // Every call will fail
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500420 auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
Matt Spinler56e08262020-01-27 16:14:23 -0600421 return this->mockHostIface->send(1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600422 };
423
Matt Spinler56e08262020-01-27 16:14:23 -0600424 EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
Matt Spinlerd438c092025-10-24 10:00:02 -0500425 .WillRepeatedly(sendFailure);
Matt Spinler7d800a42019-12-12 10:35:01 -0600426
427 dataIface.changeHostState(true);
428
429 auto pel = makePEL();
430 repo.add(pel);
431
432 // Clock more retries than necessary
Matt Spinler56e08262020-01-27 16:14:23 -0600433 runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
Matt Spinler7d800a42019-12-12 10:35:01 -0600434
435 // Should have stopped after the 15 Tries
Matt Spinler56e08262020-01-27 16:14:23 -0600436 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 15);
Matt Spinler7d800a42019-12-12 10:35:01 -0600437 EXPECT_EQ(notifier.queueSize(), 1);
438
439 // Now add another PEL, and it should start trying again
440 // though it will also eventually give up
441 pel = makePEL();
442 repo.add(pel);
443
Matt Spinler56e08262020-01-27 16:14:23 -0600444 runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
Matt Spinler7d800a42019-12-12 10:35:01 -0600445
446 // Tried an additional 15 times
Matt Spinler56e08262020-01-27 16:14:23 -0600447 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 30);
Matt Spinler7d800a42019-12-12 10:35:01 -0600448 EXPECT_EQ(notifier.queueSize(), 2);
449}
450
Matt Spinler527ff342023-06-29 12:52:34 -0500451// Test that if the command cannot be started it will give
452// up but still try again later
453TEST_F(HostNotifierTest, TestCannotStartCmd)
454{
455 sdeventplus::Event sdEvent{event};
456
457 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
458
459 // Make it behave like startCommand() fails.
460 auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
461 this->mockHostIface->cancelCmd();
462 return CmdStatus::failure;
463 };
464
465 auto sendSuccess = [this](uint32_t /*id*/, uint32_t /*size*/) {
466 return this->mockHostIface->send(0);
467 };
468
469 // Fails 16 times (1 fail + 15 retries) and
470 // then start working.
471 EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
Matt Spinlerd438c092025-10-24 10:00:02 -0500472 .WillOnce(sendFailure)
473 .WillOnce(sendFailure)
474 .WillOnce(sendFailure)
475 .WillOnce(sendFailure)
476 .WillOnce(sendFailure)
477 .WillOnce(sendFailure)
478 .WillOnce(sendFailure)
479 .WillOnce(sendFailure)
480 .WillOnce(sendFailure)
481 .WillOnce(sendFailure)
482 .WillOnce(sendFailure)
483 .WillOnce(sendFailure)
484 .WillOnce(sendFailure)
485 .WillOnce(sendFailure)
486 .WillOnce(sendFailure)
487 .WillOnce(sendFailure)
488 .WillRepeatedly(sendSuccess);
Matt Spinler527ff342023-06-29 12:52:34 -0500489
490 dataIface.changeHostState(true);
491
492 auto pel = makePEL();
493 repo.add(pel);
494
495 // Clock more retries than necessary
496 runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
497
498 // Didn't get far enough for a cmd to be processed
499 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 0);
500 EXPECT_EQ(notifier.queueSize(), 1);
501
502 // At this point, commands will work again.
503
504 pel = makePEL();
505 repo.add(pel);
506
507 // Run the events to send the PELs
508 runEvents(sdEvent, 5, mockHostIface->getReceiveRetryDelay());
509
510 // All PELs sent
511 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
512 EXPECT_EQ(notifier.queueSize(), 0);
513}
514
Matt Spinler7d800a42019-12-12 10:35:01 -0600515// Cancel an in progress command
516TEST_F(HostNotifierTest, TestCancelCmd)
517{
Matt Spinler7d800a42019-12-12 10:35:01 -0600518 sdeventplus::Event sdEvent{event};
Matt Spinler7d800a42019-12-12 10:35:01 -0600519 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
520
Matt Spinler7d800a42019-12-12 10:35:01 -0600521 dataIface.changeHostState(true);
522
523 // Add and send one PEL, but don't enter the event loop
524 // so the receive function can't run.
525 auto pel = makePEL();
526 repo.add(pel);
527
528 // Not dispatched yet
529 EXPECT_EQ(notifier.queueSize(), 1);
530
531 // Dispatch it
Matt Spinlere5f75082022-01-24 16:09:51 -0600532 runEvents(sdEvent, 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600533
534 // It was sent and off the queue
535 EXPECT_EQ(notifier.queueSize(), 0);
536
537 // This will cancel the receive
538 dataIface.changeHostState(false);
539
540 // Back on the queue
541 EXPECT_EQ(notifier.queueSize(), 1);
542
543 // Turn the host back on and make sure
544 // commands will work again
545 dataIface.changeHostState(true);
546
547 runEvents(sdEvent, 1);
548
Matt Spinler56e08262020-01-27 16:14:23 -0600549 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600550 EXPECT_EQ(notifier.queueSize(), 0);
551}
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600552
553// Test that acking a PEL persist across power cycles
554TEST_F(HostNotifierTest, TestPowerCycleAndAcks)
555{
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600556 sdeventplus::Event sdEvent{event};
557
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600558 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
559
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600560 // Add 2 PELs with host off
561 auto pel = makePEL();
562 repo.add(pel);
563 auto id1 = pel->id();
564
565 pel = makePEL();
566 repo.add(pel);
567 auto id2 = pel->id();
568
569 dataIface.changeHostState(true);
570
Matt Spinlere5f75082022-01-24 16:09:51 -0600571 runEvents(sdEvent, 3);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600572
573 // The were both sent.
Matt Spinler56e08262020-01-27 16:14:23 -0600574 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600575 EXPECT_EQ(notifier.queueSize(), 0);
576
577 dataIface.changeHostState(false);
578
579 // Those PELs weren't acked, so they will get sent again
580 EXPECT_EQ(notifier.queueSize(), 2);
581
582 // Power back on and send them again
583 dataIface.changeHostState(true);
Matt Spinlere5f75082022-01-24 16:09:51 -0600584 runEvents(sdEvent, 3);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600585
Matt Spinler56e08262020-01-27 16:14:23 -0600586 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 4);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600587 EXPECT_EQ(notifier.queueSize(), 0);
588
589 // Ack them and verify the state in the PEL.
590 notifier.ackPEL(id1);
591 notifier.ackPEL(id2);
592
593 Repository::LogID id{Repository::LogID::Pel{id1}};
594 auto data = repo.getPELData(id);
595 PEL pelFromRepo1{*data};
596 EXPECT_EQ(pelFromRepo1.hostTransmissionState(), TransmissionState::acked);
597
598 id.pelID.id = id2;
599 data = repo.getPELData(id);
600 PEL pelFromRepo2{*data};
601 EXPECT_EQ(pelFromRepo2.hostTransmissionState(), TransmissionState::acked);
602
603 // Power back off, and they should't get re-added
604 dataIface.changeHostState(false);
605
606 EXPECT_EQ(notifier.queueSize(), 0);
607}
Matt Spinler41293cb2019-12-12 13:11:09 -0600608
609// Test the host full condition
610TEST_F(HostNotifierTest, TestHostFull)
611{
612 // The full interaction with the host is:
613 // BMC: new PEL available
614 // Host: ReadPELFile (not modeled here)
615 // Host: Ack(id) (if not full), or HostFull(id)
616 // BMC: if full and any new PELs come in, don't sent them
617 // Start a timer and try again
618 // Host responds with either Ack or full
619 // and repeat
620
Matt Spinler41293cb2019-12-12 13:11:09 -0600621 sdeventplus::Event sdEvent{event};
Matt Spinler41293cb2019-12-12 13:11:09 -0600622 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
623
Matt Spinler41293cb2019-12-12 13:11:09 -0600624 dataIface.changeHostState(true);
625
626 // Add and dispatch/send one PEL
627 auto pel = makePEL();
628 auto id = pel->id();
629 repo.add(pel);
630 runEvents(sdEvent, 2);
631
Matt Spinler56e08262020-01-27 16:14:23 -0600632 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler41293cb2019-12-12 13:11:09 -0600633 EXPECT_EQ(notifier.queueSize(), 0);
634
635 // Host is full
636 notifier.setHostFull(id);
637
638 // It goes back on the queue
639 EXPECT_EQ(notifier.queueSize(), 1);
640
641 // The transmission state goes back to new
642 Repository::LogID i{Repository::LogID::Pel{id}};
643 auto data = repo.getPELData(i);
644 PEL pelFromRepo{*data};
645 EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::newPEL);
646
647 // Clock it, nothing should be sent still.
648 runEvents(sdEvent, 1);
649
Matt Spinler56e08262020-01-27 16:14:23 -0600650 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler41293cb2019-12-12 13:11:09 -0600651 EXPECT_EQ(notifier.queueSize(), 1);
652
653 // Add another PEL and clock it, still nothing sent
654 pel = makePEL();
655 repo.add(pel);
656 runEvents(sdEvent, 2);
Matt Spinler56e08262020-01-27 16:14:23 -0600657 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler41293cb2019-12-12 13:11:09 -0600658 EXPECT_EQ(notifier.queueSize(), 2);
659
660 // Let the host full timer expire to trigger a retry.
661 // Add some extra event passes just to be sure nothing new is sent.
Matt Spinler56e08262020-01-27 16:14:23 -0600662 runEvents(sdEvent, 5, mockHostIface->getHostFullRetryDelay());
Matt Spinler41293cb2019-12-12 13:11:09 -0600663
664 // The timer expiration will send just the 1, not both
Matt Spinler56e08262020-01-27 16:14:23 -0600665 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler41293cb2019-12-12 13:11:09 -0600666 EXPECT_EQ(notifier.queueSize(), 1);
667
668 // Host still full
669 notifier.setHostFull(id);
670
671 // Let the host full timer attempt again
Matt Spinler56e08262020-01-27 16:14:23 -0600672 runEvents(sdEvent, 2, mockHostIface->getHostFullRetryDelay());
673 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler41293cb2019-12-12 13:11:09 -0600674
675 // Add yet another PEL with the retry timer expired.
676 // It shouldn't get sent out.
677 pel = makePEL();
678 repo.add(pel);
679 runEvents(sdEvent, 2);
Matt Spinler56e08262020-01-27 16:14:23 -0600680 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler41293cb2019-12-12 13:11:09 -0600681
682 // The last 2 PELs still on the queue
683 EXPECT_EQ(notifier.queueSize(), 2);
684
685 // Host no longer full, it finally acks the first PEL
686 notifier.ackPEL(id);
687
688 // Now the remaining 2 PELs will be dispatched
689 runEvents(sdEvent, 3);
690
Matt Spinler56e08262020-01-27 16:14:23 -0600691 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 5);
Matt Spinler41293cb2019-12-12 13:11:09 -0600692 EXPECT_EQ(notifier.queueSize(), 0);
693}
Matt Spinlera19b6232019-12-12 13:30:14 -0600694
695// Test when the host says it was send a malformed PEL
696TEST_F(HostNotifierTest, TestBadPEL)
697{
Matt Spinlera19b6232019-12-12 13:30:14 -0600698 sdeventplus::Event sdEvent{event};
699
700 {
Matt Spinler56e08262020-01-27 16:14:23 -0600701 Repository repo1{repoPath};
702 HostNotifier notifier{repo1, dataIface, std::move(hostIface)};
Matt Spinlera19b6232019-12-12 13:30:14 -0600703
704 dataIface.changeHostState(true);
705
706 // Add a PEL and dispatch and send it
707 auto pel = makePEL();
708 auto id = pel->id();
Matt Spinler56e08262020-01-27 16:14:23 -0600709 repo1.add(pel);
Matt Spinlera19b6232019-12-12 13:30:14 -0600710
711 runEvents(sdEvent, 2);
Matt Spinler56e08262020-01-27 16:14:23 -0600712 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinlera19b6232019-12-12 13:30:14 -0600713 EXPECT_EQ(notifier.queueSize(), 0);
714
715 // The host rejected it.
716 notifier.setBadPEL(id);
717
718 // Doesn't go back on the queue
719 EXPECT_EQ(notifier.queueSize(), 0);
720
721 // Check the state was saved in the PEL itself
722 Repository::LogID i{Repository::LogID::Pel{id}};
Matt Spinler56e08262020-01-27 16:14:23 -0600723 auto data = repo1.getPELData(i);
Matt Spinlera19b6232019-12-12 13:30:14 -0600724 PEL pelFromRepo{*data};
725 EXPECT_EQ(pelFromRepo.hostTransmissionState(),
726 TransmissionState::badPEL);
727
728 dataIface.changeHostState(false);
729
730 // Ensure it doesn't go back on the queue on a power cycle
731 EXPECT_EQ(notifier.queueSize(), 0);
732 }
733
734 // Now restore the repo, and make sure it doesn't come back
735 {
Matt Spinler56e08262020-01-27 16:14:23 -0600736 Repository repo1{repoPath};
Matt Spinlera19b6232019-12-12 13:30:14 -0600737
Matt Spinler56e08262020-01-27 16:14:23 -0600738 std::unique_ptr<HostInterface> hostIface1 =
Matt Spinlera19b6232019-12-12 13:30:14 -0600739 std::make_unique<MockHostInterface>(event, dataIface);
740
Matt Spinler56e08262020-01-27 16:14:23 -0600741 HostNotifier notifier{repo1, dataIface, std::move(hostIface1)};
Matt Spinlera19b6232019-12-12 13:30:14 -0600742
743 EXPECT_EQ(notifier.queueSize(), 0);
744 }
745}
Matt Spinler24a85582020-01-27 16:40:21 -0600746
747// Test that sending PELs can be disabled
748TEST_F(HostNotifierTest, TestDisable)
749{
750 // Turn off sending the PELs except for once in the middle
751 EXPECT_CALL(dataIface, getHostPELEnablement())
752 .WillOnce(Return(false))
753 .WillOnce(Return(false))
754 .WillOnce(Return(true))
755 .WillOnce(Return(false))
756 .WillOnce(Return(false))
757 .WillOnce(Return(false));
758
759 {
760 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
761
762 // Add a PEL with the host off
763 auto pel = makePEL();
764 repo.add(pel);
765
766 // Not added to the send queue
767 EXPECT_EQ(notifier.queueSize(), 0);
768
769 dataIface.changeHostState(true);
770
771 // Try again with the host on
772 pel = makePEL();
773 repo.add(pel);
774
775 EXPECT_EQ(notifier.queueSize(), 0);
776
777 // Now getHostPELEnablement() will return true for the new PEL
778 pel = makePEL();
779 repo.add(pel);
780
781 EXPECT_EQ(notifier.queueSize(), 1);
782 }
783
784 // getHostPELEnablement is back to returning false.
785 // Create a new second instance and make sure the 3 existing PELs
786 // aren't put on the queue on startup
787 {
788 Repository repo1{repoPath};
789 std::unique_ptr<HostInterface> hostIface1 =
790 std::make_unique<MockHostInterface>(event, dataIface);
791
792 HostNotifier notifier{repo1, dataIface, std::move(hostIface1)};
793
794 EXPECT_EQ(notifier.queueSize(), 0);
795 }
796}