blob: 1906ba3178103df855a7086210d71e9900e42ca1 [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
Matt Spinler56e08262020-01-27 16:14:23 -060050 hostIface =
51 std::make_unique<NiceMock<MockHostInterface>>(event, dataIface);
52
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.
60 ON_CALL(*mockHostIface, sendNewLogCmd(_, _))
61 .WillByDefault(Invoke(send));
Matt Spinlera943b152019-12-11 14:44:50 -060062 }
63
64 ~HostNotifierTest()
65 {
66 sd_event_unref(event);
67 }
68
69 protected:
70 sd_event* event;
Matt Spinler56e08262020-01-27 16:14:23 -060071 Repository repo;
72 NiceMock<MockDataInterface> dataIface;
73 std::unique_ptr<HostInterface> hostIface;
74 MockHostInterface* mockHostIface;
Matt Spinlerf60ac272019-12-11 13:47:50 -060075};
76
77/**
78 * @brief Create PEL with the specified action flags
79 *
80 * @param[in] actionFlagsMask - Optional action flags to use
81 *
82 * @return std::unique_ptr<PEL>
83 */
84std::unique_ptr<PEL> makePEL(uint16_t actionFlagsMask = 0)
85{
86 static uint32_t obmcID = 1;
87 auto data = pelDataFactory(TestPELType::pelSimple);
88
89 data[actionFlags0Offset] |= actionFlagsMask >> 8;
90 data[actionFlags1Offset] |= actionFlagsMask & 0xFF;
91
92 auto pel = std::make_unique<PEL>(data, obmcID++);
93 pel->assignID();
94 pel->setCommitTime();
95 return pel;
96}
97
Matt Spinler7d800a42019-12-12 10:35:01 -060098/**
99 * @brief Run an iteration of the event loop.
100 *
101 * An event loop is used for:
102 * 1) timer expiration callbacks
103 * 2) Dispatches
104 * 3) host interface receive callbacks
105 *
106 * @param[in] event - The event object
107 * @param[in] numEvents - number of times to call Event::run()
108 * @param[in] timeout - timeout value for run()
109 */
110void runEvents(sdeventplus::Event& event, size_t numEvents,
111 milliseconds timeout = milliseconds(1))
112{
113 for (size_t i = 0; i < numEvents; i++)
114 {
115 event.run(timeout);
116 }
117}
118
Matt Spinlerf60ac272019-12-11 13:47:50 -0600119// Test that host state change callbacks work
120TEST_F(HostNotifierTest, TestHostStateChange)
121{
Matt Spinlerf60ac272019-12-11 13:47:50 -0600122 bool hostState = false;
123 bool called = false;
124 DataInterfaceBase::HostStateChangeFunc func = [&hostState,
125 &called](bool state) {
126 hostState = state;
127 called = true;
128 };
129
130 dataIface.subscribeToHostStateChange("test", func);
131
132 // callback called
133 dataIface.changeHostState(true);
134 EXPECT_TRUE(called);
135 EXPECT_TRUE(hostState);
136
137 // No change, not called
138 called = false;
139 dataIface.changeHostState(true);
140 EXPECT_FALSE(called);
141
142 // Called again
143 dataIface.changeHostState(false);
144 EXPECT_FALSE(hostState);
145 EXPECT_TRUE(called);
146
147 // Shouldn't get called after an unsubscribe
148 dataIface.unsubscribeFromHostStateChange("test");
149
150 called = false;
151
152 dataIface.changeHostState(true);
153 EXPECT_FALSE(called);
154}
155
Matt Spinlera943b152019-12-11 14:44:50 -0600156// Test dealing with how acked PELs are put on the
157// notification queue.
158TEST_F(HostNotifierTest, TestPolicyAckedPEL)
159{
Matt Spinlera943b152019-12-11 14:44:50 -0600160 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
161
162 auto pel = makePEL();
163 repo.add(pel);
164
165 // This is required
166 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600167 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600168
169 // Not in the repo
170 EXPECT_FALSE(notifier.enqueueRequired(42));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600171 EXPECT_FALSE(notifier.notifyRequired(42));
Matt Spinlera943b152019-12-11 14:44:50 -0600172
173 // Now set this PEL to host acked
174 repo.setPELHostTransState(pel->id(), TransmissionState::acked);
175
176 // Since it's acked, doesn't need to be enqueued or transmitted
177 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600178 EXPECT_FALSE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600179}
180
181// Test the 'don't report' PEL flag
182TEST_F(HostNotifierTest, TestPolicyDontReport)
183{
Matt Spinlera943b152019-12-11 14:44:50 -0600184 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
185
186 // dontReportToHostFlagBit
187 auto pel = makePEL(0x1000);
188
189 // Double check the action flag is still set
190 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
191 EXPECT_TRUE(actionFlags.test(dontReportToHostFlagBit));
192
193 repo.add(pel);
194
195 // Don't need to send this to the host
196 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
197}
198
199// Test that hidden PELs need notification when there
200// is no HMC.
201TEST_F(HostNotifierTest, TestPolicyHiddenNoHMC)
202{
Matt Spinlera943b152019-12-11 14:44:50 -0600203 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
204
205 // hiddenFlagBit
206 auto pel = makePEL(0x4000);
207
208 // Double check the action flag is still set
209 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
210 EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
211
212 repo.add(pel);
213
214 // Still need to enqueue this
215 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600216
217 // Still need to send it
218 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600219}
220
221// Don't need to enqueue a hidden log already acked by the HMC
222TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCAcked)
223{
Matt Spinlera943b152019-12-11 14:44:50 -0600224 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
225
226 // hiddenFlagBit
227 auto pel = makePEL(0x4000);
228
229 // Double check the action flag is still set
230 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
231 EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
232
233 repo.add(pel);
234
235 // No HMC yet, so required
236 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
237
238 repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
239
240 // Not required anymore
241 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
242}
243
Matt Spinlerf77debb2019-12-12 10:04:33 -0600244// Test that changing the HMC manage status affects
245// the policy with hidden log notification.
246TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCManaged)
247{
Matt Spinlerf77debb2019-12-12 10:04:33 -0600248 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
249
250 // hiddenFlagBit
251 auto pel = makePEL(0x4000);
252
253 repo.add(pel);
254
255 // The first time, the HMC managed is false
256 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
257
258 dataIface.setHMCManaged(true);
259
260 // This time, HMC managed is true so no need to notify
261 EXPECT_FALSE(notifier.notifyRequired(pel->id()));
262}
263
Matt Spinlerf60ac272019-12-11 13:47:50 -0600264// Test that PELs are enqueued on startup
265TEST_F(HostNotifierTest, TestStartup)
266{
Matt Spinlerf60ac272019-12-11 13:47:50 -0600267 // Give the repo 10 PELs to start with
268 for (int i = 0; i < 10; i++)
269 {
270 auto pel = makePEL();
271 repo.add(pel);
272 }
273
Matt Spinlerf60ac272019-12-11 13:47:50 -0600274 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
275
276 ASSERT_EQ(notifier.queueSize(), 10);
277
278 // Now add 10 more after the notifier is watching
279 for (int i = 0; i < 10; i++)
280 {
281 auto pel = makePEL();
282 repo.add(pel);
283 }
284
285 ASSERT_EQ(notifier.queueSize(), 20);
286}
Matt Spinler7d800a42019-12-12 10:35:01 -0600287
288// Test the simple path were PELs get sent to the host
289TEST_F(HostNotifierTest, TestSendCmd)
290{
Matt Spinler7d800a42019-12-12 10:35:01 -0600291 sdeventplus::Event sdEvent{event};
292
Matt Spinler7d800a42019-12-12 10:35:01 -0600293 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
294
Matt Spinler7d800a42019-12-12 10:35:01 -0600295 // Add a PEL with the host off
296 auto pel = makePEL();
297 repo.add(pel);
298
299 EXPECT_EQ(notifier.queueSize(), 1);
300
301 dataIface.changeHostState(true);
302
Matt Spinlere5f75082022-01-24 16:09:51 -0600303 runEvents(sdEvent, 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600304
305 // It was sent up
Matt Spinler56e08262020-01-27 16:14:23 -0600306 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600307 EXPECT_EQ(notifier.queueSize(), 0);
308
309 // Verify the state was written to the PEL.
310 Repository::LogID id{Repository::LogID::Pel{pel->id()}};
311 auto data = repo.getPELData(id);
312 PEL pelFromRepo{*data};
313 EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::sent);
314
315 // Add a few more PELs. They will get sent.
316 pel = makePEL();
317 repo.add(pel);
318
319 // Dispatch it by hitting the event loop (no commands sent yet)
320 // Don't need to test this step discretely in the future
321 runEvents(sdEvent, 1);
Matt Spinler56e08262020-01-27 16:14:23 -0600322 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600323 EXPECT_EQ(notifier.queueSize(), 0);
324
325 // Send the command
326 runEvents(sdEvent, 1);
327
Matt Spinler56e08262020-01-27 16:14:23 -0600328 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600329 EXPECT_EQ(notifier.queueSize(), 0);
330
331 pel = makePEL();
332 repo.add(pel);
333
334 // dispatch and process the command
335 runEvents(sdEvent, 2);
336
Matt Spinler56e08262020-01-27 16:14:23 -0600337 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler7d800a42019-12-12 10:35:01 -0600338 EXPECT_EQ(notifier.queueSize(), 0);
339}
340
341// Test that if the class is created with the host up,
342// it will send PELs
343TEST_F(HostNotifierTest, TestStartAfterHostUp)
344{
Matt Spinler7d800a42019-12-12 10:35:01 -0600345 // Add PELs right away
346 auto pel = makePEL();
347 repo.add(pel);
348 pel = makePEL();
349 repo.add(pel);
350
351 sdeventplus::Event sdEvent{event};
352
Matt Spinler7d800a42019-12-12 10:35:01 -0600353 // Create the HostNotifier class with the host already up
354 dataIface.changeHostState(true);
355 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
356
357 // It should start sending PELs right away
Matt Spinlere5f75082022-01-24 16:09:51 -0600358 runEvents(sdEvent, 3);
Matt Spinler7d800a42019-12-12 10:35:01 -0600359
Matt Spinler56e08262020-01-27 16:14:23 -0600360 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600361 EXPECT_EQ(notifier.queueSize(), 0);
362}
363
364// Test that a single failure will cause a retry
365TEST_F(HostNotifierTest, TestHostRetry)
366{
Matt Spinler7d800a42019-12-12 10:35:01 -0600367 sdeventplus::Event sdEvent{event};
368
Matt Spinler7d800a42019-12-12 10:35:01 -0600369 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
370
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500371 auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
Matt Spinler56e08262020-01-27 16:14:23 -0600372 return this->mockHostIface->send(1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600373 };
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500374 auto sendSuccess = [this](uint32_t /*id*/, uint32_t /*size*/) {
Matt Spinler56e08262020-01-27 16:14:23 -0600375 return this->mockHostIface->send(0);
Matt Spinler7d800a42019-12-12 10:35:01 -0600376 };
377
Matt Spinler56e08262020-01-27 16:14:23 -0600378 EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
Matt Spinler7d800a42019-12-12 10:35:01 -0600379 .WillOnce(Invoke(sendFailure))
380 .WillOnce(Invoke(sendSuccess))
381 .WillOnce(Invoke(sendSuccess));
382
383 dataIface.changeHostState(true);
384
385 auto pel = makePEL();
386 repo.add(pel);
387
388 // Dispatch and handle the command
389 runEvents(sdEvent, 2);
390
391 // The command failed, so the queue isn't empty
Matt Spinler56e08262020-01-27 16:14:23 -0600392 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600393 EXPECT_EQ(notifier.queueSize(), 1);
394
395 // Run the events again to let the timer expire and the
396 // command to be retried, which will be successful.
Matt Spinler56e08262020-01-27 16:14:23 -0600397 runEvents(sdEvent, 2, mockHostIface->getReceiveRetryDelay());
Matt Spinler7d800a42019-12-12 10:35:01 -0600398
Matt Spinler56e08262020-01-27 16:14:23 -0600399 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600400 EXPECT_EQ(notifier.queueSize(), 0);
401
402 // This one should pass with no problems
403 pel = makePEL();
404 repo.add(pel);
405
406 // Dispatch and handle the command
407 runEvents(sdEvent, 2);
408
Matt Spinler56e08262020-01-27 16:14:23 -0600409 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler7d800a42019-12-12 10:35:01 -0600410 EXPECT_EQ(notifier.queueSize(), 0);
411}
412
413// Test that all commands fail and notifier will give up
414TEST_F(HostNotifierTest, TestHardFailure)
415{
Matt Spinler7d800a42019-12-12 10:35:01 -0600416 sdeventplus::Event sdEvent{event};
417
Matt Spinler7d800a42019-12-12 10:35:01 -0600418 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
419
420 // Every call will fail
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500421 auto sendFailure = [this](uint32_t /*id*/, uint32_t /*size*/) {
Matt Spinler56e08262020-01-27 16:14:23 -0600422 return this->mockHostIface->send(1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600423 };
424
Matt Spinler56e08262020-01-27 16:14:23 -0600425 EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
Matt Spinler7d800a42019-12-12 10:35:01 -0600426 .WillRepeatedly(Invoke(sendFailure));
427
428 dataIface.changeHostState(true);
429
430 auto pel = makePEL();
431 repo.add(pel);
432
433 // Clock more retries than necessary
Matt Spinler56e08262020-01-27 16:14:23 -0600434 runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
Matt Spinler7d800a42019-12-12 10:35:01 -0600435
436 // Should have stopped after the 15 Tries
Matt Spinler56e08262020-01-27 16:14:23 -0600437 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 15);
Matt Spinler7d800a42019-12-12 10:35:01 -0600438 EXPECT_EQ(notifier.queueSize(), 1);
439
440 // Now add another PEL, and it should start trying again
441 // though it will also eventually give up
442 pel = makePEL();
443 repo.add(pel);
444
Matt Spinler56e08262020-01-27 16:14:23 -0600445 runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
Matt Spinler7d800a42019-12-12 10:35:01 -0600446
447 // Tried an additional 15 times
Matt Spinler56e08262020-01-27 16:14:23 -0600448 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 30);
Matt Spinler7d800a42019-12-12 10:35:01 -0600449 EXPECT_EQ(notifier.queueSize(), 2);
450}
451
452// Cancel an in progress command
453TEST_F(HostNotifierTest, TestCancelCmd)
454{
Matt Spinler7d800a42019-12-12 10:35:01 -0600455 sdeventplus::Event sdEvent{event};
Matt Spinler7d800a42019-12-12 10:35:01 -0600456 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
457
Matt Spinler7d800a42019-12-12 10:35:01 -0600458 dataIface.changeHostState(true);
459
460 // Add and send one PEL, but don't enter the event loop
461 // so the receive function can't run.
462 auto pel = makePEL();
463 repo.add(pel);
464
465 // Not dispatched yet
466 EXPECT_EQ(notifier.queueSize(), 1);
467
468 // Dispatch it
Matt Spinlere5f75082022-01-24 16:09:51 -0600469 runEvents(sdEvent, 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600470
471 // It was sent and off the queue
472 EXPECT_EQ(notifier.queueSize(), 0);
473
474 // This will cancel the receive
475 dataIface.changeHostState(false);
476
477 // Back on the queue
478 EXPECT_EQ(notifier.queueSize(), 1);
479
480 // Turn the host back on and make sure
481 // commands will work again
482 dataIface.changeHostState(true);
483
484 runEvents(sdEvent, 1);
485
Matt Spinler56e08262020-01-27 16:14:23 -0600486 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600487 EXPECT_EQ(notifier.queueSize(), 0);
488}
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600489
490// Test that acking a PEL persist across power cycles
491TEST_F(HostNotifierTest, TestPowerCycleAndAcks)
492{
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600493 sdeventplus::Event sdEvent{event};
494
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600495 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
496
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600497 // Add 2 PELs with host off
498 auto pel = makePEL();
499 repo.add(pel);
500 auto id1 = pel->id();
501
502 pel = makePEL();
503 repo.add(pel);
504 auto id2 = pel->id();
505
506 dataIface.changeHostState(true);
507
Matt Spinlere5f75082022-01-24 16:09:51 -0600508 runEvents(sdEvent, 3);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600509
510 // The were both sent.
Matt Spinler56e08262020-01-27 16:14:23 -0600511 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600512 EXPECT_EQ(notifier.queueSize(), 0);
513
514 dataIface.changeHostState(false);
515
516 // Those PELs weren't acked, so they will get sent again
517 EXPECT_EQ(notifier.queueSize(), 2);
518
519 // Power back on and send them again
520 dataIface.changeHostState(true);
Matt Spinlere5f75082022-01-24 16:09:51 -0600521 runEvents(sdEvent, 3);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600522
Matt Spinler56e08262020-01-27 16:14:23 -0600523 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 4);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600524 EXPECT_EQ(notifier.queueSize(), 0);
525
526 // Ack them and verify the state in the PEL.
527 notifier.ackPEL(id1);
528 notifier.ackPEL(id2);
529
530 Repository::LogID id{Repository::LogID::Pel{id1}};
531 auto data = repo.getPELData(id);
532 PEL pelFromRepo1{*data};
533 EXPECT_EQ(pelFromRepo1.hostTransmissionState(), TransmissionState::acked);
534
535 id.pelID.id = id2;
536 data = repo.getPELData(id);
537 PEL pelFromRepo2{*data};
538 EXPECT_EQ(pelFromRepo2.hostTransmissionState(), TransmissionState::acked);
539
540 // Power back off, and they should't get re-added
541 dataIface.changeHostState(false);
542
543 EXPECT_EQ(notifier.queueSize(), 0);
544}
Matt Spinler41293cb2019-12-12 13:11:09 -0600545
546// Test the host full condition
547TEST_F(HostNotifierTest, TestHostFull)
548{
549 // The full interaction with the host is:
550 // BMC: new PEL available
551 // Host: ReadPELFile (not modeled here)
552 // Host: Ack(id) (if not full), or HostFull(id)
553 // BMC: if full and any new PELs come in, don't sent them
554 // Start a timer and try again
555 // Host responds with either Ack or full
556 // and repeat
557
Matt Spinler41293cb2019-12-12 13:11:09 -0600558 sdeventplus::Event sdEvent{event};
Matt Spinler41293cb2019-12-12 13:11:09 -0600559 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
560
Matt Spinler41293cb2019-12-12 13:11:09 -0600561 dataIface.changeHostState(true);
562
563 // Add and dispatch/send one PEL
564 auto pel = makePEL();
565 auto id = pel->id();
566 repo.add(pel);
567 runEvents(sdEvent, 2);
568
Matt Spinler56e08262020-01-27 16:14:23 -0600569 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler41293cb2019-12-12 13:11:09 -0600570 EXPECT_EQ(notifier.queueSize(), 0);
571
572 // Host is full
573 notifier.setHostFull(id);
574
575 // It goes back on the queue
576 EXPECT_EQ(notifier.queueSize(), 1);
577
578 // The transmission state goes back to new
579 Repository::LogID i{Repository::LogID::Pel{id}};
580 auto data = repo.getPELData(i);
581 PEL pelFromRepo{*data};
582 EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::newPEL);
583
584 // Clock it, nothing should be sent still.
585 runEvents(sdEvent, 1);
586
Matt Spinler56e08262020-01-27 16:14:23 -0600587 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler41293cb2019-12-12 13:11:09 -0600588 EXPECT_EQ(notifier.queueSize(), 1);
589
590 // Add another PEL and clock it, still nothing sent
591 pel = makePEL();
592 repo.add(pel);
593 runEvents(sdEvent, 2);
Matt Spinler56e08262020-01-27 16:14:23 -0600594 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler41293cb2019-12-12 13:11:09 -0600595 EXPECT_EQ(notifier.queueSize(), 2);
596
597 // Let the host full timer expire to trigger a retry.
598 // Add some extra event passes just to be sure nothing new is sent.
Matt Spinler56e08262020-01-27 16:14:23 -0600599 runEvents(sdEvent, 5, mockHostIface->getHostFullRetryDelay());
Matt Spinler41293cb2019-12-12 13:11:09 -0600600
601 // The timer expiration will send just the 1, not both
Matt Spinler56e08262020-01-27 16:14:23 -0600602 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler41293cb2019-12-12 13:11:09 -0600603 EXPECT_EQ(notifier.queueSize(), 1);
604
605 // Host still full
606 notifier.setHostFull(id);
607
608 // Let the host full timer attempt again
Matt Spinler56e08262020-01-27 16:14:23 -0600609 runEvents(sdEvent, 2, mockHostIface->getHostFullRetryDelay());
610 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler41293cb2019-12-12 13:11:09 -0600611
612 // Add yet another PEL with the retry timer expired.
613 // It shouldn't get sent out.
614 pel = makePEL();
615 repo.add(pel);
616 runEvents(sdEvent, 2);
Matt Spinler56e08262020-01-27 16:14:23 -0600617 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler41293cb2019-12-12 13:11:09 -0600618
619 // The last 2 PELs still on the queue
620 EXPECT_EQ(notifier.queueSize(), 2);
621
622 // Host no longer full, it finally acks the first PEL
623 notifier.ackPEL(id);
624
625 // Now the remaining 2 PELs will be dispatched
626 runEvents(sdEvent, 3);
627
Matt Spinler56e08262020-01-27 16:14:23 -0600628 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 5);
Matt Spinler41293cb2019-12-12 13:11:09 -0600629 EXPECT_EQ(notifier.queueSize(), 0);
630}
Matt Spinlera19b6232019-12-12 13:30:14 -0600631
632// Test when the host says it was send a malformed PEL
633TEST_F(HostNotifierTest, TestBadPEL)
634{
Matt Spinlera19b6232019-12-12 13:30:14 -0600635 sdeventplus::Event sdEvent{event};
636
637 {
Matt Spinler56e08262020-01-27 16:14:23 -0600638 Repository repo1{repoPath};
639 HostNotifier notifier{repo1, dataIface, std::move(hostIface)};
Matt Spinlera19b6232019-12-12 13:30:14 -0600640
641 dataIface.changeHostState(true);
642
643 // Add a PEL and dispatch and send it
644 auto pel = makePEL();
645 auto id = pel->id();
Matt Spinler56e08262020-01-27 16:14:23 -0600646 repo1.add(pel);
Matt Spinlera19b6232019-12-12 13:30:14 -0600647
648 runEvents(sdEvent, 2);
Matt Spinler56e08262020-01-27 16:14:23 -0600649 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinlera19b6232019-12-12 13:30:14 -0600650 EXPECT_EQ(notifier.queueSize(), 0);
651
652 // The host rejected it.
653 notifier.setBadPEL(id);
654
655 // Doesn't go back on the queue
656 EXPECT_EQ(notifier.queueSize(), 0);
657
658 // Check the state was saved in the PEL itself
659 Repository::LogID i{Repository::LogID::Pel{id}};
Matt Spinler56e08262020-01-27 16:14:23 -0600660 auto data = repo1.getPELData(i);
Matt Spinlera19b6232019-12-12 13:30:14 -0600661 PEL pelFromRepo{*data};
662 EXPECT_EQ(pelFromRepo.hostTransmissionState(),
663 TransmissionState::badPEL);
664
665 dataIface.changeHostState(false);
666
667 // Ensure it doesn't go back on the queue on a power cycle
668 EXPECT_EQ(notifier.queueSize(), 0);
669 }
670
671 // Now restore the repo, and make sure it doesn't come back
672 {
Matt Spinler56e08262020-01-27 16:14:23 -0600673 Repository repo1{repoPath};
Matt Spinlera19b6232019-12-12 13:30:14 -0600674
Matt Spinler56e08262020-01-27 16:14:23 -0600675 std::unique_ptr<HostInterface> hostIface1 =
Matt Spinlera19b6232019-12-12 13:30:14 -0600676 std::make_unique<MockHostInterface>(event, dataIface);
677
Matt Spinler56e08262020-01-27 16:14:23 -0600678 HostNotifier notifier{repo1, dataIface, std::move(hostIface1)};
Matt Spinlera19b6232019-12-12 13:30:14 -0600679
680 EXPECT_EQ(notifier.queueSize(), 0);
681 }
682}
Matt Spinler24a85582020-01-27 16:40:21 -0600683
684// Test that sending PELs can be disabled
685TEST_F(HostNotifierTest, TestDisable)
686{
687 // Turn off sending the PELs except for once in the middle
688 EXPECT_CALL(dataIface, getHostPELEnablement())
689 .WillOnce(Return(false))
690 .WillOnce(Return(false))
691 .WillOnce(Return(true))
692 .WillOnce(Return(false))
693 .WillOnce(Return(false))
694 .WillOnce(Return(false));
695
696 {
697 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
698
699 // Add a PEL with the host off
700 auto pel = makePEL();
701 repo.add(pel);
702
703 // Not added to the send queue
704 EXPECT_EQ(notifier.queueSize(), 0);
705
706 dataIface.changeHostState(true);
707
708 // Try again with the host on
709 pel = makePEL();
710 repo.add(pel);
711
712 EXPECT_EQ(notifier.queueSize(), 0);
713
714 // Now getHostPELEnablement() will return true for the new PEL
715 pel = makePEL();
716 repo.add(pel);
717
718 EXPECT_EQ(notifier.queueSize(), 1);
719 }
720
721 // getHostPELEnablement is back to returning false.
722 // Create a new second instance and make sure the 3 existing PELs
723 // aren't put on the queue on startup
724 {
725 Repository repo1{repoPath};
726 std::unique_ptr<HostInterface> hostIface1 =
727 std::make_unique<MockHostInterface>(event, dataIface);
728
729 HostNotifier notifier{repo1, dataIface, std::move(hostIface1)};
730
731 EXPECT_EQ(notifier.queueSize(), 0);
732 }
733}