blob: afb5dc2fbb55691563ba89896ffbd739946e4047 [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;
32using ::testing::Return;
33namespace fs = std::filesystem;
34using namespace std::chrono;
35
36const size_t actionFlags0Offset = 66;
37const size_t actionFlags1Offset = 67;
38
39class HostNotifierTest : public CleanPELFiles
40{
Matt Spinlera943b152019-12-11 14:44:50 -060041 public:
42 HostNotifierTest()
43 {
44 auto r = sd_event_default(&event);
45 EXPECT_TRUE(r >= 0);
46 }
47
48 ~HostNotifierTest()
49 {
50 sd_event_unref(event);
51 }
52
53 protected:
54 sd_event* event;
Matt Spinlerf60ac272019-12-11 13:47:50 -060055};
56
57/**
58 * @brief Create PEL with the specified action flags
59 *
60 * @param[in] actionFlagsMask - Optional action flags to use
61 *
62 * @return std::unique_ptr<PEL>
63 */
64std::unique_ptr<PEL> makePEL(uint16_t actionFlagsMask = 0)
65{
66 static uint32_t obmcID = 1;
67 auto data = pelDataFactory(TestPELType::pelSimple);
68
69 data[actionFlags0Offset] |= actionFlagsMask >> 8;
70 data[actionFlags1Offset] |= actionFlagsMask & 0xFF;
71
72 auto pel = std::make_unique<PEL>(data, obmcID++);
73 pel->assignID();
74 pel->setCommitTime();
75 return pel;
76}
77
Matt Spinler7d800a42019-12-12 10:35:01 -060078/**
79 * @brief Run an iteration of the event loop.
80 *
81 * An event loop is used for:
82 * 1) timer expiration callbacks
83 * 2) Dispatches
84 * 3) host interface receive callbacks
85 *
86 * @param[in] event - The event object
87 * @param[in] numEvents - number of times to call Event::run()
88 * @param[in] timeout - timeout value for run()
89 */
90void runEvents(sdeventplus::Event& event, size_t numEvents,
91 milliseconds timeout = milliseconds(1))
92{
93 for (size_t i = 0; i < numEvents; i++)
94 {
95 event.run(timeout);
96 }
97}
98
Matt Spinlerf60ac272019-12-11 13:47:50 -060099// Test that host state change callbacks work
100TEST_F(HostNotifierTest, TestHostStateChange)
101{
102 MockDataInterface dataIface;
103
104 bool hostState = false;
105 bool called = false;
106 DataInterfaceBase::HostStateChangeFunc func = [&hostState,
107 &called](bool state) {
108 hostState = state;
109 called = true;
110 };
111
112 dataIface.subscribeToHostStateChange("test", func);
113
114 // callback called
115 dataIface.changeHostState(true);
116 EXPECT_TRUE(called);
117 EXPECT_TRUE(hostState);
118
119 // No change, not called
120 called = false;
121 dataIface.changeHostState(true);
122 EXPECT_FALSE(called);
123
124 // Called again
125 dataIface.changeHostState(false);
126 EXPECT_FALSE(hostState);
127 EXPECT_TRUE(called);
128
129 // Shouldn't get called after an unsubscribe
130 dataIface.unsubscribeFromHostStateChange("test");
131
132 called = false;
133
134 dataIface.changeHostState(true);
135 EXPECT_FALSE(called);
136}
137
Matt Spinlera943b152019-12-11 14:44:50 -0600138// Test dealing with how acked PELs are put on the
139// notification queue.
140TEST_F(HostNotifierTest, TestPolicyAckedPEL)
141{
142 Repository repo{repoPath};
143 MockDataInterface dataIface;
144
145 std::unique_ptr<HostInterface> hostIface =
146 std::make_unique<MockHostInterface>(event, dataIface);
147
148 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
149
150 auto pel = makePEL();
151 repo.add(pel);
152
153 // This is required
154 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600155 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600156
157 // Not in the repo
158 EXPECT_FALSE(notifier.enqueueRequired(42));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600159 EXPECT_FALSE(notifier.notifyRequired(42));
Matt Spinlera943b152019-12-11 14:44:50 -0600160
161 // Now set this PEL to host acked
162 repo.setPELHostTransState(pel->id(), TransmissionState::acked);
163
164 // Since it's acked, doesn't need to be enqueued or transmitted
165 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600166 EXPECT_FALSE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600167}
168
169// Test the 'don't report' PEL flag
170TEST_F(HostNotifierTest, TestPolicyDontReport)
171{
172 Repository repo{repoPath};
173 MockDataInterface dataIface;
174
175 std::unique_ptr<HostInterface> hostIface =
176 std::make_unique<MockHostInterface>(event, dataIface);
177
178 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
179
180 // dontReportToHostFlagBit
181 auto pel = makePEL(0x1000);
182
183 // Double check the action flag is still set
184 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
185 EXPECT_TRUE(actionFlags.test(dontReportToHostFlagBit));
186
187 repo.add(pel);
188
189 // Don't need to send this to the host
190 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
191}
192
193// Test that hidden PELs need notification when there
194// is no HMC.
195TEST_F(HostNotifierTest, TestPolicyHiddenNoHMC)
196{
197 Repository repo{repoPath};
198 MockDataInterface dataIface;
199
200 std::unique_ptr<HostInterface> hostIface =
201 std::make_unique<MockHostInterface>(event, dataIface);
202
203 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{
224 Repository repo{repoPath};
225 MockDataInterface dataIface;
226
227 std::unique_ptr<HostInterface> hostIface =
228 std::make_unique<MockHostInterface>(event, dataIface);
229
230 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
231
232 // hiddenFlagBit
233 auto pel = makePEL(0x4000);
234
235 // Double check the action flag is still set
236 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
237 EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
238
239 repo.add(pel);
240
241 // No HMC yet, so required
242 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
243
244 repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
245
246 // Not required anymore
247 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
248}
249
Matt Spinlerf77debb2019-12-12 10:04:33 -0600250// Test that changing the HMC manage status affects
251// the policy with hidden log notification.
252TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCManaged)
253{
254 Repository repo{repoPath};
255 MockDataInterface dataIface;
256
257 std::unique_ptr<HostInterface> hostIface =
258 std::make_unique<MockHostInterface>(event, dataIface);
259
260 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
261
262 // hiddenFlagBit
263 auto pel = makePEL(0x4000);
264
265 repo.add(pel);
266
267 // The first time, the HMC managed is false
268 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
269
270 dataIface.setHMCManaged(true);
271
272 // This time, HMC managed is true so no need to notify
273 EXPECT_FALSE(notifier.notifyRequired(pel->id()));
274}
275
Matt Spinlerf60ac272019-12-11 13:47:50 -0600276// Test that PELs are enqueued on startup
277TEST_F(HostNotifierTest, TestStartup)
278{
279 Repository repo{repoPath};
280 MockDataInterface dataIface;
281
282 // Give the repo 10 PELs to start with
283 for (int i = 0; i < 10; i++)
284 {
285 auto pel = makePEL();
286 repo.add(pel);
287 }
288
Matt Spinlerf60ac272019-12-11 13:47:50 -0600289 std::unique_ptr<HostInterface> hostIface =
290 std::make_unique<MockHostInterface>(event, dataIface);
291
292 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
293
294 ASSERT_EQ(notifier.queueSize(), 10);
295
296 // Now add 10 more after the notifier is watching
297 for (int i = 0; i < 10; i++)
298 {
299 auto pel = makePEL();
300 repo.add(pel);
301 }
302
303 ASSERT_EQ(notifier.queueSize(), 20);
304}
Matt Spinler7d800a42019-12-12 10:35:01 -0600305
306// Test the simple path were PELs get sent to the host
307TEST_F(HostNotifierTest, TestSendCmd)
308{
309 Repository repo{repoPath};
310 MockDataInterface dataIface;
311
312 sdeventplus::Event sdEvent{event};
313
314 std::unique_ptr<HostInterface> hostIface =
315 std::make_unique<MockHostInterface>(event, dataIface);
316
317 MockHostInterface& mockHostIface =
318 reinterpret_cast<MockHostInterface&>(*hostIface);
319
320 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
321
322 auto send = [&mockHostIface](uint32_t id, uint32_t size) {
323 return mockHostIface.send(0);
324 };
325
326 EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _))
327 .WillRepeatedly(Invoke(send));
328
329 // Add a PEL with the host off
330 auto pel = makePEL();
331 repo.add(pel);
332
333 EXPECT_EQ(notifier.queueSize(), 1);
334
335 dataIface.changeHostState(true);
336
337 runEvents(sdEvent, 1);
338
339 // It was sent up
340 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 1);
341 EXPECT_EQ(notifier.queueSize(), 0);
342
343 // Verify the state was written to the PEL.
344 Repository::LogID id{Repository::LogID::Pel{pel->id()}};
345 auto data = repo.getPELData(id);
346 PEL pelFromRepo{*data};
347 EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::sent);
348
349 // Add a few more PELs. They will get sent.
350 pel = makePEL();
351 repo.add(pel);
352
353 // Dispatch it by hitting the event loop (no commands sent yet)
354 // Don't need to test this step discretely in the future
355 runEvents(sdEvent, 1);
356 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 1);
357 EXPECT_EQ(notifier.queueSize(), 0);
358
359 // Send the command
360 runEvents(sdEvent, 1);
361
362 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 2);
363 EXPECT_EQ(notifier.queueSize(), 0);
364
365 pel = makePEL();
366 repo.add(pel);
367
368 // dispatch and process the command
369 runEvents(sdEvent, 2);
370
371 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 3);
372 EXPECT_EQ(notifier.queueSize(), 0);
373}
374
375// Test that if the class is created with the host up,
376// it will send PELs
377TEST_F(HostNotifierTest, TestStartAfterHostUp)
378{
379 Repository repo{repoPath};
380 MockDataInterface dataIface;
381
382 // Add PELs right away
383 auto pel = makePEL();
384 repo.add(pel);
385 pel = makePEL();
386 repo.add(pel);
387
388 sdeventplus::Event sdEvent{event};
389
390 std::unique_ptr<HostInterface> hostIface =
391 std::make_unique<MockHostInterface>(event, dataIface);
392
393 MockHostInterface& mockHostIface =
394 reinterpret_cast<MockHostInterface&>(*hostIface);
395
396 auto send = [&mockHostIface](uint32_t id, uint32_t size) {
397 return mockHostIface.send(0);
398 };
399
400 EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _))
401 .WillRepeatedly(Invoke(send));
402
403 // Create the HostNotifier class with the host already up
404 dataIface.changeHostState(true);
405 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
406
407 // It should start sending PELs right away
408 runEvents(sdEvent, 2);
409
410 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 2);
411 EXPECT_EQ(notifier.queueSize(), 0);
412}
413
414// Test that a single failure will cause a retry
415TEST_F(HostNotifierTest, TestHostRetry)
416{
417 Repository repo{repoPath};
418 MockDataInterface dataIface;
419
420 sdeventplus::Event sdEvent{event};
421
422 std::unique_ptr<HostInterface> hostIface =
423 std::make_unique<MockHostInterface>(event, dataIface);
424
425 MockHostInterface& mockHostIface =
426 reinterpret_cast<MockHostInterface&>(*hostIface);
427
428 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
429
430 auto sendFailure = [&mockHostIface](uint32_t id, uint32_t size) {
431 return mockHostIface.send(1);
432 };
433 auto sendSuccess = [&mockHostIface](uint32_t id, uint32_t size) {
434 return mockHostIface.send(0);
435 };
436
437 EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _))
438 .WillOnce(Invoke(sendFailure))
439 .WillOnce(Invoke(sendSuccess))
440 .WillOnce(Invoke(sendSuccess));
441
442 dataIface.changeHostState(true);
443
444 auto pel = makePEL();
445 repo.add(pel);
446
447 // Dispatch and handle the command
448 runEvents(sdEvent, 2);
449
450 // The command failed, so the queue isn't empty
451 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 1);
452 EXPECT_EQ(notifier.queueSize(), 1);
453
454 // Run the events again to let the timer expire and the
455 // command to be retried, which will be successful.
456 runEvents(sdEvent, 2, mockHostIface.getReceiveRetryDelay());
457
458 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 2);
459 EXPECT_EQ(notifier.queueSize(), 0);
460
461 // This one should pass with no problems
462 pel = makePEL();
463 repo.add(pel);
464
465 // Dispatch and handle the command
466 runEvents(sdEvent, 2);
467
468 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 3);
469 EXPECT_EQ(notifier.queueSize(), 0);
470}
471
472// Test that all commands fail and notifier will give up
473TEST_F(HostNotifierTest, TestHardFailure)
474{
475 Repository repo{repoPath};
476 MockDataInterface dataIface;
477
478 sdeventplus::Event sdEvent{event};
479
480 std::unique_ptr<HostInterface> hostIface =
481 std::make_unique<MockHostInterface>(event, dataIface);
482
483 MockHostInterface& mockHostIface =
484 reinterpret_cast<MockHostInterface&>(*hostIface);
485
486 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
487
488 // Every call will fail
489 auto sendFailure = [&mockHostIface](uint32_t id, uint32_t size) {
490 return mockHostIface.send(1);
491 };
492
493 EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _))
494 .WillRepeatedly(Invoke(sendFailure));
495
496 dataIface.changeHostState(true);
497
498 auto pel = makePEL();
499 repo.add(pel);
500
501 // Clock more retries than necessary
502 runEvents(sdEvent, 40, mockHostIface.getReceiveRetryDelay());
503
504 // Should have stopped after the 15 Tries
505 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 15);
506 EXPECT_EQ(notifier.queueSize(), 1);
507
508 // Now add another PEL, and it should start trying again
509 // though it will also eventually give up
510 pel = makePEL();
511 repo.add(pel);
512
513 runEvents(sdEvent, 40, mockHostIface.getReceiveRetryDelay());
514
515 // Tried an additional 15 times
516 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 30);
517 EXPECT_EQ(notifier.queueSize(), 2);
518}
519
520// Cancel an in progress command
521TEST_F(HostNotifierTest, TestCancelCmd)
522{
523 Repository repo{repoPath};
524 MockDataInterface dataIface;
525
526 sdeventplus::Event sdEvent{event};
527
528 std::unique_ptr<HostInterface> hostIface =
529 std::make_unique<MockHostInterface>(event, dataIface);
530
531 MockHostInterface& mockHostIface =
532 reinterpret_cast<MockHostInterface&>(*hostIface);
533
534 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
535
536 auto send = [&mockHostIface](uint32_t id, uint32_t size) {
537 return mockHostIface.send(0);
538 };
539
540 EXPECT_CALL(mockHostIface, sendNewLogCmd(_, _))
541 .WillRepeatedly(Invoke(send));
542
543 dataIface.changeHostState(true);
544
545 // Add and send one PEL, but don't enter the event loop
546 // so the receive function can't run.
547 auto pel = makePEL();
548 repo.add(pel);
549
550 // Not dispatched yet
551 EXPECT_EQ(notifier.queueSize(), 1);
552
553 // Dispatch it
554 runEvents(sdEvent, 1);
555
556 // It was sent and off the queue
557 EXPECT_EQ(notifier.queueSize(), 0);
558
559 // This will cancel the receive
560 dataIface.changeHostState(false);
561
562 // Back on the queue
563 EXPECT_EQ(notifier.queueSize(), 1);
564
565 // Turn the host back on and make sure
566 // commands will work again
567 dataIface.changeHostState(true);
568
569 runEvents(sdEvent, 1);
570
571 EXPECT_EQ(mockHostIface.numCmdsProcessed(), 1);
572 EXPECT_EQ(notifier.queueSize(), 0);
573}