blob: 215f7259895e0d544be5090bb6d2fe6c4d1b9a86 [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);
22 enableAccumulateTimer(true);
23}
24
25void Context::enableAccumulateTimer(bool enable)
26{
27 // fetch the timeout from the SOL manager
28 std::chrono::microseconds interval =
29 std::get<sol::Manager&>(singletonPool).accumulateInterval;
30 if (enable)
31 {
32 accumulateTimer.expires_after(interval);
33 accumulateTimer.async_wait([this](const boost::system::error_code& ec) {
34 if (!ec)
35 {
36 charAccTimerHandler();
37 }
38 });
39 }
40 else
41 {
42 accumulateTimer.cancel();
43 }
44}
45
46void Context::enableRetryTimer(bool enable)
47{
48 if (enable)
49 {
50 // fetch the timeout from the SOL manager
51 std::chrono::microseconds interval =
52 std::get<sol::Manager&>(singletonPool).retryInterval;
53 retryTimer.expires_after(interval);
54 retryTimer.async_wait([this](const boost::system::error_code& ec) {
55 if (!ec)
56 {
57 retryTimerHandler();
58 }
59 });
60 }
61 else
62 {
63 retryTimer.cancel();
64 }
65}
66
Vernon Mauery9e801a22018-10-12 13:20:49 -070067void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
68 uint8_t count, bool status,
Vernon Mauery70fd29c2017-11-30 13:11:43 -080069 const std::vector<uint8_t>& input)
Tom Josephfbcac2e2017-03-14 18:15:07 +053070{
71 uint8_t respAckSeqNum = 0;
72 uint8_t acceptedCount = 0;
73 auto ack = false;
74
75 /*
76 * Check if the Inbound sequence number is same as the expected one.
77 * If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple
78 * outstanding sequence numbers are not supported in this version of the SOL
79 * specification. Retried packets use the same sequence number as the first
80 * packet.
81 */
Vernon Mauery9e801a22018-10-12 13:20:49 -070082 if (seqNum && (seqNum != seqNums.get(true)))
Tom Josephfbcac2e2017-03-14 18:15:07 +053083 {
84 log<level::INFO>("Out of sequence SOL packet - packet is dropped");
85 return;
86 }
87
88 /*
89 * Check if the expected ACK/NACK sequence number is same as the
90 * ACK/NACK sequence number in the packet. If packet ACK/NACK sequence
91 * number is 0, then it is an informational packet. No request packet being
92 * ACK'd or NACK'd.
93 */
94 if (ackSeqNum && (ackSeqNum != seqNums.get(false)))
95 {
96 log<level::INFO>("Out of sequence ack number - SOL packet is dropped");
97 return;
98 }
99
100 /*
101 * Retry the SOL payload packet in the following conditions:
102 *
103 * a) NACK in Operation/Status
104 * b) Accepted Character Count does not match with the sent out SOL payload
105 * c) Non-zero Packet ACK/NACK Sequence Number
106 */
107 if (status || ((count != expectedCharCount) && ackSeqNum))
108 {
109 resendPayload(noClear);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800110 enableRetryTimer(false);
111 enableRetryTimer(true);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530112 return;
113 }
114 /*
115 * Clear the sent data once the acknowledgment sequence number matches
116 * and the expected character count matches.
117 */
118 else if ((count == expectedCharCount) && ackSeqNum)
119 {
120 // Clear the Host Console Buffer
121 std::get<sol::Manager&>(singletonPool).dataBuffer.erase(count);
122
123 // Once it is acknowledged stop the retry interval timer
Vernon Mauery6f353e82018-11-09 08:43:24 -0800124 enableRetryTimer(false);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530125
126 retryCounter = maxRetryCount;
127 expectedCharCount = 0;
128 payloadCache.clear();
129 }
130
131 // Write character data to the Host Console
132 if (!input.empty() && seqNum)
133 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700134 auto rc =
135 std::get<sol::Manager&>(singletonPool).writeConsoleSocket(input);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530136 if (rc)
137 {
138 log<level::ERR>("Writing to console socket descriptor failed");
139 ack = true;
140 }
141 else
142 {
143 respAckSeqNum = seqNum;
144 ack = false;
145 acceptedCount = input.size();
146 }
147 }
Tom Joseph694fc0c2017-08-21 11:47:25 +0530148 /*
149 * SOL payload with no character data and valid sequence number can be used
150 * as method to keep the SOL session active.
151 */
152 else if (input.empty() && seqNum)
153 {
154 respAckSeqNum = seqNum;
155 }
Tom Josephfbcac2e2017-03-14 18:15:07 +0530156
157 if (seqNum != 0)
158 {
159 seqNums.incInboundSeqNum();
160 prepareResponse(respAckSeqNum, acceptedCount, ack);
161 }
162 else
163 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800164 enableAccumulateTimer(true);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530165 }
166}
167
Tom Joseph75e15db2017-03-14 18:16:22 +0530168void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack)
169{
Vernon Mauery9e801a22018-10-12 13:20:49 -0700170 auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
Tom Joseph75e15db2017-03-14 18:16:22 +0530171
172 /* Sent a ACK only response */
173 if (payloadCache.size() != 0 || (bufferSize < sendThreshold))
174 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800175 enableAccumulateTimer(true);
Tom Joseph75e15db2017-03-14 18:16:22 +0530176
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800177 std::vector<uint8_t> outPayload(sizeof(Payload));
Tom Joseph75e15db2017-03-14 18:16:22 +0530178 auto response = reinterpret_cast<Payload*>(outPayload.data());
179 response->packetSeqNum = 0;
180 response->packetAckSeqNum = ackSeqNum;
181 response->acceptedCharCount = count;
182 response->outOperation.ack = ack;
183 sendPayload(outPayload);
184 return;
185 }
186
187 auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
188 payloadCache.resize(sizeof(Payload) + readSize);
189 auto response = reinterpret_cast<Payload*>(payloadCache.data());
190 response->packetAckSeqNum = ackSeqNum;
191 response->acceptedCharCount = count;
192 response->outOperation.ack = ack;
193 response->packetSeqNum = seqNums.incOutboundSeqNum();
194
Tom Joseph75e15db2017-03-14 18:16:22 +0530195 auto handle = std::get<sol::Manager&>(singletonPool).dataBuffer.read();
196 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
197 expectedCharCount = readSize;
198
Vernon Mauery6f353e82018-11-09 08:43:24 -0800199 enableRetryTimer(true);
200 enableAccumulateTimer(false);
Tom Joseph75e15db2017-03-14 18:16:22 +0530201
202 sendPayload(payloadCache);
203}
204
Tom Joseph73063142017-03-14 18:20:20 +0530205int Context::sendOutboundPayload()
206{
207 if (payloadCache.size() != 0)
208 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800209 enableAccumulateTimer(true);
Tom Joseph73063142017-03-14 18:20:20 +0530210 return -1;
211 }
212
213 auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
214 auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
215
216 payloadCache.resize(sizeof(Payload) + readSize);
217 auto response = reinterpret_cast<Payload*>(payloadCache.data());
218 response->packetAckSeqNum = 0;
219 response->acceptedCharCount = 0;
220 response->outOperation.ack = false;
221 response->packetSeqNum = seqNums.incOutboundSeqNum();
222
223 auto handle = std::get<sol::Manager&>(singletonPool).dataBuffer.read();
224 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
225 expectedCharCount = readSize;
226
Vernon Mauery6f353e82018-11-09 08:43:24 -0800227 enableRetryTimer(true);
228 enableAccumulateTimer(false);
Tom Joseph73063142017-03-14 18:20:20 +0530229
230 sendPayload(payloadCache);
231
232 return 0;
233}
234
Tom Joseph75e15db2017-03-14 18:16:22 +0530235void Context::resendPayload(bool clear)
Tom Josephfbcac2e2017-03-14 18:15:07 +0530236{
Tom Joseph2fd466f2017-03-30 08:16:47 +0530237 sendPayload(payloadCache);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530238
Vernon Mauery9e801a22018-10-12 13:20:49 -0700239 if (clear)
Tom Joseph2fd466f2017-03-30 08:16:47 +0530240 {
241 payloadCache.clear();
242 expectedCharCount = 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -0700243 std::get<sol::Manager&>(singletonPool)
244 .dataBuffer.erase(expectedCharCount);
Tom Joseph2fd466f2017-03-30 08:16:47 +0530245 }
Tom Josephfbcac2e2017-03-14 18:15:07 +0530246}
247
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800248void Context::sendPayload(const std::vector<uint8_t>& out) const
Tom Josephfbcac2e2017-03-14 18:15:07 +0530249{
Tom Joseph22596f22017-03-31 10:52:27 +0530250 message::Handler msgHandler(session->channelPtr, sessionID);
251
252 msgHandler.sendSOLPayload(out);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530253}
254
Vernon Mauery6f353e82018-11-09 08:43:24 -0800255void Context::charAccTimerHandler()
256{
257 auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
258
259 try
260 {
261 if (bufferSize > 0)
262 {
263 int rc = sendOutboundPayload();
264 if (rc == 0)
265 {
266 return;
267 }
268 }
269 enableAccumulateTimer(true);
270 }
271 catch (std::exception& e)
272 {
273 log<level::ERR>(e.what());
274 }
275}
276
277void Context::retryTimerHandler()
278{
279 try
280 {
281 if (retryCounter)
282 {
283 --retryCounter;
284 enableRetryTimer(true);
285 resendPayload(sol::Context::noClear);
286 }
287 else
288 {
289 retryCounter = maxRetryCount;
290 resendPayload(sol::Context::clear);
291 enableRetryTimer(false);
292 enableAccumulateTimer(true);
293 }
294 }
295 catch (std::exception& e)
296 {
297 log<level::ERR>(e.what());
298 }
299}
Tom Josephfbcac2e2017-03-14 18:15:07 +0530300} // namespace sol