19e801a2bSVernon Mauery #include "sol_context.hpp"
29e801a2bSVernon Mauery
3fbcac2e7STom Joseph #include "main.hpp"
42085ae07SVernon Mauery #include "message_handler.hpp"
5fbcac2e7STom Joseph #include "sd_event_loop.hpp"
62085ae07SVernon Mauery #include "sessions_manager.hpp"
7fbcac2e7STom Joseph #include "sol_manager.hpp"
8fbcac2e7STom Joseph
97b7f25f7SGeorge Liu #include <errno.h>
107b7f25f7SGeorge Liu
117b7f25f7SGeorge Liu #include <phosphor-logging/lg2.hpp>
129e801a2bSVernon Mauery
13fbcac2e7STom Joseph namespace sol
14fbcac2e7STom Joseph {
15fbcac2e7STom Joseph using namespace phosphor::logging;
16fbcac2e7STom Joseph
Context(std::shared_ptr<boost::asio::io_context> io,uint8_t maxRetryCount,uint8_t sendThreshold,uint8_t instance,session::SessionID sessionID)176f353e86SVernon Mauery Context::Context(std::shared_ptr<boost::asio::io_context> io,
186f353e86SVernon Mauery uint8_t maxRetryCount, uint8_t sendThreshold, uint8_t instance,
196f353e86SVernon Mauery session::SessionID sessionID) :
20*8425624aSPatrick Williams accumulateTimer(*io), retryTimer(*io), maxRetryCount(maxRetryCount),
21*8425624aSPatrick Williams retryCounter(maxRetryCount), sendThreshold(sendThreshold),
22*8425624aSPatrick Williams payloadInstance(instance), sessionID(sessionID)
236f353e86SVernon Mauery {
242085ae07SVernon Mauery session = session::Manager::get().getSession(sessionID);
25a6ad5e16SVernon Mauery }
26a6ad5e16SVernon Mauery
makeContext(std::shared_ptr<boost::asio::io_context> io,uint8_t maxRetryCount,uint8_t sendThreshold,uint8_t instance,session::SessionID sessionID)27*8425624aSPatrick Williams std::shared_ptr<Context> Context::makeContext(
28*8425624aSPatrick Williams std::shared_ptr<boost::asio::io_context> io, uint8_t maxRetryCount,
29*8425624aSPatrick Williams uint8_t sendThreshold, uint8_t instance, session::SessionID sessionID)
30a6ad5e16SVernon Mauery {
31a6ad5e16SVernon Mauery auto ctx = std::make_shared<Context>(io, maxRetryCount, sendThreshold,
32a6ad5e16SVernon Mauery instance, sessionID);
33a6ad5e16SVernon Mauery ctx->enableAccumulateTimer(true);
34a6ad5e16SVernon Mauery return ctx;
356f353e86SVernon Mauery }
366f353e86SVernon Mauery
enableAccumulateTimer(bool enable)376f353e86SVernon Mauery void Context::enableAccumulateTimer(bool enable)
386f353e86SVernon Mauery {
396f353e86SVernon Mauery // fetch the timeout from the SOL manager
402085ae07SVernon Mauery std::chrono::microseconds interval = sol::Manager::get().accumulateInterval;
4115535143STang Yiwei
426f353e86SVernon Mauery if (enable)
436f353e86SVernon Mauery {
4415535143STang Yiwei auto bufferSize = sol::Manager::get().dataBuffer.size();
4515535143STang Yiwei if (bufferSize > sendThreshold)
4615535143STang Yiwei {
47426fcab8SKonstantin Aladyshev try
485499bf86SJian Zhang {
49426fcab8SKonstantin Aladyshev int rc = sendOutboundPayload();
50426fcab8SKonstantin Aladyshev if (rc == 0)
51426fcab8SKonstantin Aladyshev {
5215535143STang Yiwei return;
5315535143STang Yiwei }
54426fcab8SKonstantin Aladyshev }
55426fcab8SKonstantin Aladyshev catch (const std::exception& e)
56426fcab8SKonstantin Aladyshev {
57426fcab8SKonstantin Aladyshev lg2::error(
58426fcab8SKonstantin Aladyshev "Failed to call the sendOutboundPayload method: {ERROR}",
59426fcab8SKonstantin Aladyshev "ERROR", e);
60426fcab8SKonstantin Aladyshev return;
61426fcab8SKonstantin Aladyshev }
62426fcab8SKonstantin Aladyshev }
636f353e86SVernon Mauery accumulateTimer.expires_after(interval);
64426fcab8SKonstantin Aladyshev std::weak_ptr<Context> weakRef = weak_from_this();
65a6ad5e16SVernon Mauery accumulateTimer.async_wait(
66a6ad5e16SVernon Mauery [weakRef](const boost::system::error_code& ec) {
67a6ad5e16SVernon Mauery std::shared_ptr<Context> self = weakRef.lock();
68a6ad5e16SVernon Mauery if (!ec && self)
696f353e86SVernon Mauery {
70a6ad5e16SVernon Mauery self->charAccTimerHandler();
716f353e86SVernon Mauery }
726f353e86SVernon Mauery });
736f353e86SVernon Mauery }
746f353e86SVernon Mauery else
756f353e86SVernon Mauery {
766f353e86SVernon Mauery accumulateTimer.cancel();
776f353e86SVernon Mauery }
786f353e86SVernon Mauery }
796f353e86SVernon Mauery
enableRetryTimer(bool enable)806f353e86SVernon Mauery void Context::enableRetryTimer(bool enable)
816f353e86SVernon Mauery {
826f353e86SVernon Mauery if (enable)
836f353e86SVernon Mauery {
846f353e86SVernon Mauery // fetch the timeout from the SOL manager
852085ae07SVernon Mauery std::chrono::microseconds interval = sol::Manager::get().retryInterval;
866f353e86SVernon Mauery retryTimer.expires_after(interval);
87a6ad5e16SVernon Mauery std::weak_ptr<Context> weakRef = weak_from_this();
88a6ad5e16SVernon Mauery retryTimer.async_wait([weakRef](const boost::system::error_code& ec) {
89a6ad5e16SVernon Mauery std::shared_ptr<Context> self = weakRef.lock();
90a6ad5e16SVernon Mauery if (!ec && self)
916f353e86SVernon Mauery {
92a6ad5e16SVernon Mauery self->retryTimerHandler();
936f353e86SVernon Mauery }
946f353e86SVernon Mauery });
956f353e86SVernon Mauery }
966f353e86SVernon Mauery else
976f353e86SVernon Mauery {
986f353e86SVernon Mauery retryTimer.cancel();
996f353e86SVernon Mauery }
1006f353e86SVernon Mauery }
1016f353e86SVernon Mauery
processInboundPayload(uint8_t seqNum,uint8_t ackSeqNum,uint8_t count,bool status,bool isBreak,const std::vector<uint8_t> & input)1029e801a2bSVernon Mauery void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
103ec437414STingting Chen uint8_t count, bool status, bool isBreak,
10470fd29cfSVernon Mauery const std::vector<uint8_t>& input)
105fbcac2e7STom Joseph {
106fbcac2e7STom Joseph uint8_t respAckSeqNum = 0;
107fbcac2e7STom Joseph uint8_t acceptedCount = 0;
108fbcac2e7STom Joseph auto ack = false;
109fbcac2e7STom Joseph
110fbcac2e7STom Joseph /*
111fbcac2e7STom Joseph * Check if the Inbound sequence number is same as the expected one.
112fbcac2e7STom Joseph * If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple
113fbcac2e7STom Joseph * outstanding sequence numbers are not supported in this version of the SOL
114fbcac2e7STom Joseph * specification. Retried packets use the same sequence number as the first
115fbcac2e7STom Joseph * packet.
116fbcac2e7STom Joseph */
117fbcac2e7STom Joseph if (seqNum && (seqNum != seqNums.get(true)))
118fbcac2e7STom Joseph {
1197b7f25f7SGeorge Liu lg2::info("Out of sequence SOL packet - packet is dropped");
120fbcac2e7STom Joseph return;
121fbcac2e7STom Joseph }
122fbcac2e7STom Joseph
123fbcac2e7STom Joseph /*
124fbcac2e7STom Joseph * Check if the expected ACK/NACK sequence number is same as the
125fbcac2e7STom Joseph * ACK/NACK sequence number in the packet. If packet ACK/NACK sequence
126fbcac2e7STom Joseph * number is 0, then it is an informational packet. No request packet being
127fbcac2e7STom Joseph * ACK'd or NACK'd.
128fbcac2e7STom Joseph */
129fbcac2e7STom Joseph if (ackSeqNum && (ackSeqNum != seqNums.get(false)))
130fbcac2e7STom Joseph {
1317b7f25f7SGeorge Liu lg2::info("Out of sequence ack number - SOL packet is dropped");
132fbcac2e7STom Joseph return;
133fbcac2e7STom Joseph }
134fbcac2e7STom Joseph
135fbcac2e7STom Joseph /*
136fbcac2e7STom Joseph * Retry the SOL payload packet in the following conditions:
137fbcac2e7STom Joseph *
138fbcac2e7STom Joseph * a) NACK in Operation/Status
139fbcac2e7STom Joseph * b) Accepted Character Count does not match with the sent out SOL payload
140fbcac2e7STom Joseph * c) Non-zero Packet ACK/NACK Sequence Number
141fbcac2e7STom Joseph */
142fbcac2e7STom Joseph if (status || ((count != expectedCharCount) && ackSeqNum))
143fbcac2e7STom Joseph {
144fbcac2e7STom Joseph resendPayload(noClear);
1456f353e86SVernon Mauery enableRetryTimer(false);
1466f353e86SVernon Mauery enableRetryTimer(true);
147fbcac2e7STom Joseph return;
148fbcac2e7STom Joseph }
149fbcac2e7STom Joseph /*
150fbcac2e7STom Joseph * Clear the sent data once the acknowledgment sequence number matches
151fbcac2e7STom Joseph * and the expected character count matches.
152fbcac2e7STom Joseph */
153fbcac2e7STom Joseph else if ((count == expectedCharCount) && ackSeqNum)
154fbcac2e7STom Joseph {
155fbcac2e7STom Joseph // Clear the Host Console Buffer
1562085ae07SVernon Mauery sol::Manager::get().dataBuffer.erase(count);
157fbcac2e7STom Joseph
158fbcac2e7STom Joseph // Once it is acknowledged stop the retry interval timer
1596f353e86SVernon Mauery enableRetryTimer(false);
160fbcac2e7STom Joseph
161fbcac2e7STom Joseph retryCounter = maxRetryCount;
162fbcac2e7STom Joseph expectedCharCount = 0;
163fbcac2e7STom Joseph payloadCache.clear();
164fbcac2e7STom Joseph }
165fbcac2e7STom Joseph
166ec437414STingting Chen if (isBreak && seqNum)
167ec437414STingting Chen {
168ec437414STingting Chen lg2::info("Writing break to console socket descriptor");
169ec437414STingting Chen constexpr uint8_t sysrqValue = 72; // use this to notify sol server
170ec437414STingting Chen const std::vector<uint8_t> test{sysrqValue};
171ec437414STingting Chen auto ret = sol::Manager::get().writeConsoleSocket(test, isBreak);
172ec437414STingting Chen if (ret)
173ec437414STingting Chen {
174ec437414STingting Chen lg2::error("Writing to console socket descriptor failed: {ERROR}",
175ec437414STingting Chen "ERROR", strerror(errno));
176ec437414STingting Chen }
177ec437414STingting Chen }
178ec437414STingting Chen
179ec437414STingting Chen isBreak = false;
180fbcac2e7STom Joseph // Write character data to the Host Console
181fbcac2e7STom Joseph if (!input.empty() && seqNum)
182fbcac2e7STom Joseph {
183ec437414STingting Chen auto rc = sol::Manager::get().writeConsoleSocket(input, isBreak);
184fbcac2e7STom Joseph if (rc)
185fbcac2e7STom Joseph {
1867b7f25f7SGeorge Liu lg2::error("Writing to console socket descriptor failed: {ERROR}",
1877b7f25f7SGeorge Liu "ERROR", strerror(errno));
188fbcac2e7STom Joseph ack = true;
189fbcac2e7STom Joseph }
190fbcac2e7STom Joseph else
191fbcac2e7STom Joseph {
192fbcac2e7STom Joseph respAckSeqNum = seqNum;
193fbcac2e7STom Joseph ack = false;
194fbcac2e7STom Joseph acceptedCount = input.size();
195fbcac2e7STom Joseph }
196fbcac2e7STom Joseph }
197694fc0cfSTom Joseph /*
198694fc0cfSTom Joseph * SOL payload with no character data and valid sequence number can be used
199694fc0cfSTom Joseph * as method to keep the SOL session active.
200694fc0cfSTom Joseph */
201694fc0cfSTom Joseph else if (input.empty() && seqNum)
202694fc0cfSTom Joseph {
203694fc0cfSTom Joseph respAckSeqNum = seqNum;
204694fc0cfSTom Joseph }
205fbcac2e7STom Joseph
206fbcac2e7STom Joseph if (seqNum != 0)
207fbcac2e7STom Joseph {
208fbcac2e7STom Joseph seqNums.incInboundSeqNum();
209fbcac2e7STom Joseph prepareResponse(respAckSeqNum, acceptedCount, ack);
210fbcac2e7STom Joseph }
211fbcac2e7STom Joseph else
212fbcac2e7STom Joseph {
2136f353e86SVernon Mauery enableAccumulateTimer(true);
214fbcac2e7STom Joseph }
215fbcac2e7STom Joseph }
216fbcac2e7STom Joseph
prepareResponse(uint8_t ackSeqNum,uint8_t count,bool ack)21775e15db4STom Joseph void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack)
21875e15db4STom Joseph {
2192085ae07SVernon Mauery auto bufferSize = sol::Manager::get().dataBuffer.size();
22075e15db4STom Joseph
22175e15db4STom Joseph /* Sent a ACK only response */
22275e15db4STom Joseph if (payloadCache.size() != 0 || (bufferSize < sendThreshold))
22375e15db4STom Joseph {
2246f353e86SVernon Mauery enableAccumulateTimer(true);
22575e15db4STom Joseph
22670fd29cfSVernon Mauery std::vector<uint8_t> outPayload(sizeof(Payload));
22775e15db4STom Joseph auto response = reinterpret_cast<Payload*>(outPayload.data());
22875e15db4STom Joseph response->packetSeqNum = 0;
22975e15db4STom Joseph response->packetAckSeqNum = ackSeqNum;
23075e15db4STom Joseph response->acceptedCharCount = count;
23175e15db4STom Joseph response->outOperation.ack = ack;
23275e15db4STom Joseph sendPayload(outPayload);
23375e15db4STom Joseph return;
23475e15db4STom Joseph }
23575e15db4STom Joseph
23675e15db4STom Joseph auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
23775e15db4STom Joseph payloadCache.resize(sizeof(Payload) + readSize);
23875e15db4STom Joseph auto response = reinterpret_cast<Payload*>(payloadCache.data());
23975e15db4STom Joseph response->packetAckSeqNum = ackSeqNum;
24075e15db4STom Joseph response->acceptedCharCount = count;
24175e15db4STom Joseph response->outOperation.ack = ack;
24275e15db4STom Joseph response->packetSeqNum = seqNums.incOutboundSeqNum();
24375e15db4STom Joseph
2442085ae07SVernon Mauery auto handle = sol::Manager::get().dataBuffer.read();
24575e15db4STom Joseph std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
24675e15db4STom Joseph expectedCharCount = readSize;
24775e15db4STom Joseph
2486f353e86SVernon Mauery enableRetryTimer(true);
2496f353e86SVernon Mauery enableAccumulateTimer(false);
25075e15db4STom Joseph
25175e15db4STom Joseph sendPayload(payloadCache);
25275e15db4STom Joseph }
25375e15db4STom Joseph
sendOutboundPayload()2547306314fSTom Joseph int Context::sendOutboundPayload()
2557306314fSTom Joseph {
2567306314fSTom Joseph if (payloadCache.size() != 0)
2577306314fSTom Joseph {
2587306314fSTom Joseph return -1;
2597306314fSTom Joseph }
2607306314fSTom Joseph
2612085ae07SVernon Mauery auto bufferSize = sol::Manager::get().dataBuffer.size();
2627306314fSTom Joseph auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
2637306314fSTom Joseph
2647306314fSTom Joseph payloadCache.resize(sizeof(Payload) + readSize);
2657306314fSTom Joseph auto response = reinterpret_cast<Payload*>(payloadCache.data());
2667306314fSTom Joseph response->packetAckSeqNum = 0;
2677306314fSTom Joseph response->acceptedCharCount = 0;
2687306314fSTom Joseph response->outOperation.ack = false;
2697306314fSTom Joseph response->packetSeqNum = seqNums.incOutboundSeqNum();
2707306314fSTom Joseph
2712085ae07SVernon Mauery auto handle = sol::Manager::get().dataBuffer.read();
2727306314fSTom Joseph std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
2737306314fSTom Joseph expectedCharCount = readSize;
2747306314fSTom Joseph
2756f353e86SVernon Mauery enableRetryTimer(true);
2766f353e86SVernon Mauery enableAccumulateTimer(false);
2777306314fSTom Joseph
2787306314fSTom Joseph sendPayload(payloadCache);
2797306314fSTom Joseph
2807306314fSTom Joseph return 0;
2817306314fSTom Joseph }
2827306314fSTom Joseph
resendPayload(bool clear)28375e15db4STom Joseph void Context::resendPayload(bool clear)
284fbcac2e7STom Joseph {
2852fd466f4STom Joseph sendPayload(payloadCache);
286fbcac2e7STom Joseph
2872fd466f4STom Joseph if (clear)
2882fd466f4STom Joseph {
2892fd466f4STom Joseph payloadCache.clear();
2902fd466f4STom Joseph expectedCharCount = 0;
2912085ae07SVernon Mauery sol::Manager::get().dataBuffer.erase(expectedCharCount);
2922fd466f4STom Joseph }
293fbcac2e7STom Joseph }
294fbcac2e7STom Joseph
sendPayload(const std::vector<uint8_t> & out) const29570fd29cfSVernon Mauery void Context::sendPayload(const std::vector<uint8_t>& out) const
296fbcac2e7STom Joseph {
29722596f21STom Joseph message::Handler msgHandler(session->channelPtr, sessionID);
29822596f21STom Joseph
29922596f21STom Joseph msgHandler.sendSOLPayload(out);
300fbcac2e7STom Joseph }
301fbcac2e7STom Joseph
charAccTimerHandler()3026f353e86SVernon Mauery void Context::charAccTimerHandler()
3036f353e86SVernon Mauery {
3042085ae07SVernon Mauery auto bufferSize = sol::Manager::get().dataBuffer.size();
3056f353e86SVernon Mauery
3066f353e86SVernon Mauery try
3076f353e86SVernon Mauery {
3086f353e86SVernon Mauery if (bufferSize > 0)
3096f353e86SVernon Mauery {
3106f353e86SVernon Mauery int rc = sendOutboundPayload();
3116f353e86SVernon Mauery if (rc == 0)
3126f353e86SVernon Mauery {
3136f353e86SVernon Mauery return;
3146f353e86SVernon Mauery }
3156f353e86SVernon Mauery }
3166f353e86SVernon Mauery enableAccumulateTimer(true);
3176f353e86SVernon Mauery }
31812d199b2SPatrick Williams catch (const std::exception& e)
3196f353e86SVernon Mauery {
3207b7f25f7SGeorge Liu lg2::error("Failed to call the sendOutboundPayload method: {ERROR}",
3217b7f25f7SGeorge Liu "ERROR", e);
3226f353e86SVernon Mauery }
3236f353e86SVernon Mauery }
3246f353e86SVernon Mauery
retryTimerHandler()3256f353e86SVernon Mauery void Context::retryTimerHandler()
3266f353e86SVernon Mauery {
3276f353e86SVernon Mauery try
3286f353e86SVernon Mauery {
3296f353e86SVernon Mauery if (retryCounter)
3306f353e86SVernon Mauery {
3316f353e86SVernon Mauery --retryCounter;
3326f353e86SVernon Mauery enableRetryTimer(true);
3336f353e86SVernon Mauery resendPayload(sol::Context::noClear);
3346f353e86SVernon Mauery }
3356f353e86SVernon Mauery else
3366f353e86SVernon Mauery {
3376f353e86SVernon Mauery retryCounter = maxRetryCount;
3386f353e86SVernon Mauery resendPayload(sol::Context::clear);
3396f353e86SVernon Mauery enableRetryTimer(false);
3406f353e86SVernon Mauery enableAccumulateTimer(true);
3416f353e86SVernon Mauery }
3426f353e86SVernon Mauery }
34312d199b2SPatrick Williams catch (const std::exception& e)
3446f353e86SVernon Mauery {
3457b7f25f7SGeorge Liu lg2::error("Failed to retry timer: {ERROR}", "ERROR", e);
3466f353e86SVernon Mauery }
3476f353e86SVernon Mauery }
348fbcac2e7STom Joseph } // namespace sol
349