blob: f9e0418ae676baddcc1bb10b4649c5e6dec129bc [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 "host_notifier.hpp"
17
18#include <phosphor-logging/log.hpp>
19
20namespace openpower::pels
21{
22
23const auto subscriptionName = "PELHostNotifier";
24
25using namespace phosphor::logging;
26
27HostNotifier::HostNotifier(Repository& repo, DataInterfaceBase& dataIface,
28 std::unique_ptr<HostInterface> hostIface) :
29 _repo(repo),
Matt Spinlerf869fcf2019-12-11 15:02:20 -060030 _dataIface(dataIface), _hostIface(std::move(hostIface)),
31 _retryTimer(_hostIface->getEvent(),
32 std::bind(std::mem_fn(&HostNotifier::retryTimerExpired), this))
Matt Spinlerf60ac272019-12-11 13:47:50 -060033{
34 // Subscribe to be told about new PELs.
35 _repo.subscribeToAdds(subscriptionName,
36 std::bind(std::mem_fn(&HostNotifier::newLogCallback),
37 this, std::placeholders::_1));
38
39 // Add any existing PELs to the queue to send them if necessary.
40 _repo.for_each(std::bind(std::mem_fn(&HostNotifier::addPELToQueue), this,
41 std::placeholders::_1));
42
43 // Subscribe to be told about host state changes.
44 _dataIface.subscribeToHostStateChange(
45 subscriptionName,
46 std::bind(std::mem_fun(&HostNotifier::hostStateChange), this,
47 std::placeholders::_1));
48
49 // Set the function to call when the async reponse is received.
50 _hostIface->setResponseFunction(
51 std::bind(std::mem_fn(&HostNotifier::commandResponse), this,
52 std::placeholders::_1));
53
54 // Start sending logs if the host is running
55 if (!_pelQueue.empty() && _dataIface.isHostUp())
56 {
57 doNewLogNotify();
58 }
59}
60
61HostNotifier::~HostNotifier()
62{
63 _repo.unsubscribeFromAdds(subscriptionName);
64 _dataIface.unsubscribeFromHostStateChange(subscriptionName);
65}
66
67bool HostNotifier::addPELToQueue(const PEL& pel)
68{
69 if (enqueueRequired(pel.id()))
70 {
71 _pelQueue.push_back(pel.id());
72 }
73
74 // Return false so that Repo::for_each keeps going.
75 return false;
76}
77
78bool HostNotifier::enqueueRequired(uint32_t id) const
79{
80 bool required = true;
Matt Spinlera943b152019-12-11 14:44:50 -060081 Repository::LogID i{Repository::LogID::Pel{id}};
82
83 if (auto attributes = _repo.getPELAttributes(i); attributes)
84 {
85 auto a = attributes.value().get();
86
87 if ((a.hostState == TransmissionState::acked) ||
88 (a.hostState == TransmissionState::badPEL))
89 {
90 required = false;
91 }
92 else if (a.actionFlags.test(hiddenFlagBit) &&
93 (a.hmcState == TransmissionState::acked))
94 {
95 required = false;
96 }
97 else if (a.actionFlags.test(dontReportToHostFlagBit))
98 {
99 required = false;
100 }
101 }
102 else
103 {
104 using namespace phosphor::logging;
105 log<level::ERR>("Host Enqueue: Unable to find PEL ID in repository",
106 entry("PEL_ID=0x%X", id));
107 required = false;
108 }
Matt Spinlerf60ac272019-12-11 13:47:50 -0600109
110 return required;
111}
112
113void HostNotifier::newLogCallback(const PEL& pel)
114{
115 if (!enqueueRequired(pel.id()))
116 {
117 return;
118 }
119
120 _pelQueue.push_back(pel.id());
121
122 // TODO: Check if a send is needed now
123}
124
125void HostNotifier::doNewLogNotify()
126{
127}
128
129void HostNotifier::hostStateChange(bool hostUp)
130{
Matt Spinler3019c6f2019-12-11 15:24:45 -0600131 _retryCount = 0;
132
133 if (hostUp && !_pelQueue.empty())
134 {
135 doNewLogNotify();
136 }
137 else if (!hostUp)
138 {
139 stopCommand();
140
141 // Reset the state on any PELs that were sent but not acked back
142 // to new so they'll get sent again.
143 for (auto id : _sentPELs)
144 {
145 _pelQueue.push_back(id);
146 _repo.setPELHostTransState(id, TransmissionState::newPEL);
147 }
148
149 _sentPELs.clear();
150 }
Matt Spinlerf60ac272019-12-11 13:47:50 -0600151}
152
153void HostNotifier::commandResponse(ResponseStatus status)
154{
Matt Spinlerf869fcf2019-12-11 15:02:20 -0600155 auto id = _inProgressPEL;
156 _inProgressPEL = 0;
157
158 if (status == ResponseStatus::success)
159 {
160 _retryCount = 0;
161
162 _sentPELs.push_back(id);
163
164 _repo.setPELHostTransState(id, TransmissionState::sent);
165
166 if (!_pelQueue.empty())
167 {
168 doNewLogNotify();
169 }
170 }
171 else
172 {
173 log<level::ERR>("PLDM command response failure",
174 entry("PEL_ID=0x%X", id));
175 // Retry
176 _pelQueue.push_front(id);
177 _retryTimer.restartOnce(_hostIface->getReceiveRetryDelay());
178 }
179}
180
181void HostNotifier::retryTimerExpired()
182{
183 if (_dataIface.isHostUp())
184 {
185 log<level::INFO>("Attempting command retry",
186 entry("PEL_ID=0x%X", _pelQueue.front()));
187 _retryCount++;
188 doNewLogNotify();
189 }
Matt Spinlerf60ac272019-12-11 13:47:50 -0600190}
191
Matt Spinler3019c6f2019-12-11 15:24:45 -0600192void HostNotifier::stopCommand()
193{
194 _retryCount = 0;
195
196 if (_inProgressPEL != 0)
197 {
198 _pelQueue.push_front(_inProgressPEL);
199 _inProgressPEL = 0;
200 }
201
202 if (_retryTimer.isEnabled())
203 {
204 _retryTimer.setEnabled(false);
205 }
206
207 if (_hostIface->cmdInProgress())
208 {
209 _hostIface->cancelCmd();
210 }
211}
212
Matt Spinlerf60ac272019-12-11 13:47:50 -0600213} // namespace openpower::pels