blob: 3b2b892b79067af2c759d93c6d921bc6756fda34 [file] [log] [blame]
Vernon Mauery9e801a22018-10-12 13:20:49 -07001#include "sol_context.hpp"
2
Tom Josephfbcac2e2017-03-14 18:15:07 +05303#include "main.hpp"
4#include "sd_event_loop.hpp"
Tom Josephfbcac2e2017-03-14 18:15:07 +05305#include "sol_manager.hpp"
6
Vernon Mauery9e801a22018-10-12 13:20:49 -07007#include <phosphor-logging/log.hpp>
8
Tom Josephfbcac2e2017-03-14 18:15:07 +05309namespace sol
10{
Tom Josephfbcac2e2017-03-14 18:15:07 +053011using namespace phosphor::logging;
12
Vernon Mauery6f353e82018-11-09 08:43:24 -080013Context::Context(std::shared_ptr<boost::asio::io_context> io,
14 uint8_t maxRetryCount, uint8_t sendThreshold, uint8_t instance,
15 session::SessionID sessionID) :
16 accumulateTimer(*io),
17 retryTimer(*io), maxRetryCount(maxRetryCount), retryCounter(maxRetryCount),
18 sendThreshold(sendThreshold), payloadInstance(instance),
19 sessionID(sessionID)
20{
21 session = std::get<session::Manager&>(singletonPool).getSession(sessionID);
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080022}
23
24std::shared_ptr<Context>
25 Context::makeContext(std::shared_ptr<boost::asio::io_context> io,
26 uint8_t maxRetryCount, uint8_t sendThreshold,
27 uint8_t instance, session::SessionID sessionID)
28{
29 auto ctx = std::make_shared<Context>(io, maxRetryCount, sendThreshold,
30 instance, sessionID);
31 ctx->enableAccumulateTimer(true);
32 return ctx;
Vernon Mauery6f353e82018-11-09 08:43:24 -080033}
34
35void Context::enableAccumulateTimer(bool enable)
36{
37 // fetch the timeout from the SOL manager
38 std::chrono::microseconds interval =
39 std::get<sol::Manager&>(singletonPool).accumulateInterval;
40 if (enable)
41 {
42 accumulateTimer.expires_after(interval);
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080043 std::weak_ptr<Context> weakRef = weak_from_this();
44 accumulateTimer.async_wait(
45 [weakRef](const boost::system::error_code& ec) {
46 std::shared_ptr<Context> self = weakRef.lock();
47 if (!ec && self)
48 {
49 self->charAccTimerHandler();
50 }
51 });
Vernon Mauery6f353e82018-11-09 08:43:24 -080052 }
53 else
54 {
55 accumulateTimer.cancel();
56 }
57}
58
59void Context::enableRetryTimer(bool enable)
60{
61 if (enable)
62 {
63 // fetch the timeout from the SOL manager
64 std::chrono::microseconds interval =
65 std::get<sol::Manager&>(singletonPool).retryInterval;
66 retryTimer.expires_after(interval);
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080067 std::weak_ptr<Context> weakRef = weak_from_this();
68 retryTimer.async_wait([weakRef](const boost::system::error_code& ec) {
69 std::shared_ptr<Context> self = weakRef.lock();
70 if (!ec && self)
Vernon Mauery6f353e82018-11-09 08:43:24 -080071 {
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080072 self->retryTimerHandler();
Vernon Mauery6f353e82018-11-09 08:43:24 -080073 }
74 });
75 }
76 else
77 {
78 retryTimer.cancel();
79 }
80}
81
Vernon Mauery9e801a22018-10-12 13:20:49 -070082void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
83 uint8_t count, bool status,
Vernon Mauery70fd29c2017-11-30 13:11:43 -080084 const std::vector<uint8_t>& input)
Tom Josephfbcac2e2017-03-14 18:15:07 +053085{
86 uint8_t respAckSeqNum = 0;
87 uint8_t acceptedCount = 0;
88 auto ack = false;
89
90 /*
91 * Check if the Inbound sequence number is same as the expected one.
92 * If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple
93 * outstanding sequence numbers are not supported in this version of the SOL
94 * specification. Retried packets use the same sequence number as the first
95 * packet.
96 */
Vernon Mauery9e801a22018-10-12 13:20:49 -070097 if (seqNum && (seqNum != seqNums.get(true)))
Tom Josephfbcac2e2017-03-14 18:15:07 +053098 {
99 log<level::INFO>("Out of sequence SOL packet - packet is dropped");
100 return;
101 }
102
103 /*
104 * Check if the expected ACK/NACK sequence number is same as the
105 * ACK/NACK sequence number in the packet. If packet ACK/NACK sequence
106 * number is 0, then it is an informational packet. No request packet being
107 * ACK'd or NACK'd.
108 */
109 if (ackSeqNum && (ackSeqNum != seqNums.get(false)))
110 {
111 log<level::INFO>("Out of sequence ack number - SOL packet is dropped");
112 return;
113 }
114
115 /*
116 * Retry the SOL payload packet in the following conditions:
117 *
118 * a) NACK in Operation/Status
119 * b) Accepted Character Count does not match with the sent out SOL payload
120 * c) Non-zero Packet ACK/NACK Sequence Number
121 */
122 if (status || ((count != expectedCharCount) && ackSeqNum))
123 {
124 resendPayload(noClear);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800125 enableRetryTimer(false);
126 enableRetryTimer(true);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530127 return;
128 }
129 /*
130 * Clear the sent data once the acknowledgment sequence number matches
131 * and the expected character count matches.
132 */
133 else if ((count == expectedCharCount) && ackSeqNum)
134 {
135 // Clear the Host Console Buffer
136 std::get<sol::Manager&>(singletonPool).dataBuffer.erase(count);
137
138 // Once it is acknowledged stop the retry interval timer
Vernon Mauery6f353e82018-11-09 08:43:24 -0800139 enableRetryTimer(false);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530140
141 retryCounter = maxRetryCount;
142 expectedCharCount = 0;
143 payloadCache.clear();
144 }
145
146 // Write character data to the Host Console
147 if (!input.empty() && seqNum)
148 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700149 auto rc =
150 std::get<sol::Manager&>(singletonPool).writeConsoleSocket(input);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530151 if (rc)
152 {
153 log<level::ERR>("Writing to console socket descriptor failed");
154 ack = true;
155 }
156 else
157 {
158 respAckSeqNum = seqNum;
159 ack = false;
160 acceptedCount = input.size();
161 }
162 }
Tom Joseph694fc0c2017-08-21 11:47:25 +0530163 /*
164 * SOL payload with no character data and valid sequence number can be used
165 * as method to keep the SOL session active.
166 */
167 else if (input.empty() && seqNum)
168 {
169 respAckSeqNum = seqNum;
170 }
Tom Josephfbcac2e2017-03-14 18:15:07 +0530171
172 if (seqNum != 0)
173 {
174 seqNums.incInboundSeqNum();
175 prepareResponse(respAckSeqNum, acceptedCount, ack);
176 }
177 else
178 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800179 enableAccumulateTimer(true);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530180 }
181}
182
Tom Joseph75e15db2017-03-14 18:16:22 +0530183void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack)
184{
Vernon Mauery9e801a22018-10-12 13:20:49 -0700185 auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
Tom Joseph75e15db2017-03-14 18:16:22 +0530186
187 /* Sent a ACK only response */
188 if (payloadCache.size() != 0 || (bufferSize < sendThreshold))
189 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800190 enableAccumulateTimer(true);
Tom Joseph75e15db2017-03-14 18:16:22 +0530191
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800192 std::vector<uint8_t> outPayload(sizeof(Payload));
Tom Joseph75e15db2017-03-14 18:16:22 +0530193 auto response = reinterpret_cast<Payload*>(outPayload.data());
194 response->packetSeqNum = 0;
195 response->packetAckSeqNum = ackSeqNum;
196 response->acceptedCharCount = count;
197 response->outOperation.ack = ack;
198 sendPayload(outPayload);
199 return;
200 }
201
202 auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
203 payloadCache.resize(sizeof(Payload) + readSize);
204 auto response = reinterpret_cast<Payload*>(payloadCache.data());
205 response->packetAckSeqNum = ackSeqNum;
206 response->acceptedCharCount = count;
207 response->outOperation.ack = ack;
208 response->packetSeqNum = seqNums.incOutboundSeqNum();
209
Tom Joseph75e15db2017-03-14 18:16:22 +0530210 auto handle = std::get<sol::Manager&>(singletonPool).dataBuffer.read();
211 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
212 expectedCharCount = readSize;
213
Vernon Mauery6f353e82018-11-09 08:43:24 -0800214 enableRetryTimer(true);
215 enableAccumulateTimer(false);
Tom Joseph75e15db2017-03-14 18:16:22 +0530216
217 sendPayload(payloadCache);
218}
219
Tom Joseph73063142017-03-14 18:20:20 +0530220int Context::sendOutboundPayload()
221{
222 if (payloadCache.size() != 0)
223 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800224 enableAccumulateTimer(true);
Tom Joseph73063142017-03-14 18:20:20 +0530225 return -1;
226 }
227
228 auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
229 auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
230
231 payloadCache.resize(sizeof(Payload) + readSize);
232 auto response = reinterpret_cast<Payload*>(payloadCache.data());
233 response->packetAckSeqNum = 0;
234 response->acceptedCharCount = 0;
235 response->outOperation.ack = false;
236 response->packetSeqNum = seqNums.incOutboundSeqNum();
237
238 auto handle = std::get<sol::Manager&>(singletonPool).dataBuffer.read();
239 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
240 expectedCharCount = readSize;
241
Vernon Mauery6f353e82018-11-09 08:43:24 -0800242 enableRetryTimer(true);
243 enableAccumulateTimer(false);
Tom Joseph73063142017-03-14 18:20:20 +0530244
245 sendPayload(payloadCache);
246
247 return 0;
248}
249
Tom Joseph75e15db2017-03-14 18:16:22 +0530250void Context::resendPayload(bool clear)
Tom Josephfbcac2e2017-03-14 18:15:07 +0530251{
Tom Joseph2fd466f2017-03-30 08:16:47 +0530252 sendPayload(payloadCache);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530253
Vernon Mauery9e801a22018-10-12 13:20:49 -0700254 if (clear)
Tom Joseph2fd466f2017-03-30 08:16:47 +0530255 {
256 payloadCache.clear();
257 expectedCharCount = 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -0700258 std::get<sol::Manager&>(singletonPool)
259 .dataBuffer.erase(expectedCharCount);
Tom Joseph2fd466f2017-03-30 08:16:47 +0530260 }
Tom Josephfbcac2e2017-03-14 18:15:07 +0530261}
262
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800263void Context::sendPayload(const std::vector<uint8_t>& out) const
Tom Josephfbcac2e2017-03-14 18:15:07 +0530264{
Tom Joseph22596f22017-03-31 10:52:27 +0530265 message::Handler msgHandler(session->channelPtr, sessionID);
266
267 msgHandler.sendSOLPayload(out);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530268}
269
Vernon Mauery6f353e82018-11-09 08:43:24 -0800270void Context::charAccTimerHandler()
271{
272 auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
273
274 try
275 {
276 if (bufferSize > 0)
277 {
278 int rc = sendOutboundPayload();
279 if (rc == 0)
280 {
281 return;
282 }
283 }
284 enableAccumulateTimer(true);
285 }
286 catch (std::exception& e)
287 {
288 log<level::ERR>(e.what());
289 }
290}
291
292void Context::retryTimerHandler()
293{
294 try
295 {
296 if (retryCounter)
297 {
298 --retryCounter;
299 enableRetryTimer(true);
300 resendPayload(sol::Context::noClear);
301 }
302 else
303 {
304 retryCounter = maxRetryCount;
305 resendPayload(sol::Context::clear);
306 enableRetryTimer(false);
307 enableAccumulateTimer(true);
308 }
309 }
310 catch (std::exception& e)
311 {
312 log<level::ERR>(e.what());
313 }
314}
Tom Josephfbcac2e2017-03-14 18:15:07 +0530315} // namespace sol