netipmid: make Handler asynchronous
The dbus call to the main ipmid queue was up to this point synchronous,
which means it blocks all other networking and execution until the main
queue returns (which may be on the order of seconds for some commands).
This is an unacceptable delay, especially when this queue is responsible
for timely updates of SOL traffic.
This turns the call into an asynchronous one by leveraging shared
pointers and an optional action on destruction. So as long as a
reference to the Handler object exists, it will live on, waiting to send
its response. Once the async dbus call has returned and set the reply in
the Handler, it will drop the reference to the shared pointer and the
destructor will send out the response over the channel.
Tested-by: Run multiple sessions at the same time while monitoring dbus
traffic. See that the requests and responses may be
interleaved instead of serial.
Change-Id: I16fca8dc3d13624eeb1592ec36d1a9af6575f115
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
diff --git a/command_table.cpp b/command_table.cpp
index e911bc6..04e1ac2 100644
--- a/command_table.cpp
+++ b/command_table.cpp
@@ -41,14 +41,12 @@
command = std::move(entry);
}
-std::vector<uint8_t> Table::executeCommand(uint32_t inCommand,
- std::vector<uint8_t>& commandData,
- const message::Handler& handler)
+void Table::executeCommand(uint32_t inCommand,
+ std::vector<uint8_t>& commandData,
+ std::shared_ptr<message::Handler> handler)
{
using namespace std::chrono_literals;
- std::vector<uint8_t> response;
-
auto iterator = commandTable.find(inCommand);
if (iterator == commandTable.end())
@@ -57,54 +55,50 @@
auto bus = getSdBus();
// forward the request onto the main ipmi queue
- auto method = bus->new_method_call(
- "xyz.openbmc_project.Ipmi.Host", "/xyz/openbmc_project/Ipmi",
- "xyz.openbmc_project.Ipmi.Server", "execute");
+ using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t,
+ std::vector<uint8_t>>;
uint8_t lun = command.lun();
uint8_t netFn = command.netFn();
uint8_t cmd = command.cmd();
std::shared_ptr<session::Session> session =
std::get<session::Manager&>(singletonPool)
- .getSession(handler.sessionID);
+ .getSession(handler->sessionID);
std::map<std::string, ipmi::Value> options = {
{"userId", ipmi::Value(ipmi::ipmiUserGetUserId(session->userName))},
{"privilege", ipmi::Value(static_cast<int>(session->curPrivLevel))},
};
- method.append(netFn, lun, cmd, commandData, options);
- using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t, uint8_t,
- std::vector<uint8_t>>;
- IpmiDbusRspType rspTuple;
- try
- {
- auto reply = bus->call(method);
- reply.read(rspTuple);
- }
- catch (const sdbusplus::exception::SdBusError& e)
- {
- response.push_back(IPMI_CC_UNSPECIFIED_ERROR);
- log<level::ERR>("Error sending command to ipmi queue");
- elog<InternalFailure>();
- }
- auto& [rnetFn, rlun, rcmd, cc, responseData] = rspTuple;
- if (uint8_t(netFn + 1) != rnetFn || rlun != lun || rcmd != cmd)
- {
- response.push_back(IPMI_CC_UNSPECIFIED_ERROR);
- log<level::ERR>("DBus call/response mismatch from ipmi queue");
- elog<InternalFailure>();
- }
- else
- {
- response.reserve(1 + responseData.size());
- response.push_back(cc);
- response.insert(response.end(), responseData.begin(),
- responseData.end());
- }
+ bus->async_method_call(
+ [handler, this](const boost::system::error_code& ec,
+ const IpmiDbusRspType& response) {
+ if (!ec)
+ {
+ const uint8_t& cc = std::get<3>(response);
+ const std::vector<uint8_t>& responseData =
+ std::get<4>(response);
+ std::vector<uint8_t> payload;
+ payload.reserve(1 + responseData.size());
+ payload.push_back(cc);
+ payload.insert(payload.end(), responseData.begin(),
+ responseData.end());
+ handler->outPayload = std::move(payload);
+ }
+ else
+ {
+ std::vector<uint8_t> payload;
+ payload.push_back(IPMI_CC_UNSPECIFIED_ERROR);
+ handler->outPayload = std::move(payload);
+ }
+ },
+ "xyz.openbmc_project.Ipmi.Host", "/xyz/openbmc_project/Ipmi",
+ "xyz.openbmc_project.Ipmi.Server", "execute", netFn, lun, cmd,
+ commandData, options);
}
else
{
auto start = std::chrono::steady_clock::now();
- response = iterator->second->executeCommand(commandData, handler);
+ handler->outPayload =
+ iterator->second->executeCommand(commandData, handler);
auto end = std::chrono::steady_clock::now();
@@ -119,17 +113,16 @@
entry("DELAY=%d", elapsedSeconds.count()));
}
}
- return response;
}
std::vector<uint8_t>
NetIpmidEntry::executeCommand(std::vector<uint8_t>& commandData,
- const message::Handler& handler)
+ std::shared_ptr<message::Handler> handler)
{
std::vector<uint8_t> errResponse;
// Check if the command qualifies to be run prior to establishing a session
- if (!sessionless && (handler.sessionID == session::SESSION_ZERO))
+ if (!sessionless && (handler->sessionID == session::SESSION_ZERO))
{
errResponse.resize(1);
errResponse[0] = IPMI_CC_INSUFFICIENT_PRIVILEGE;
@@ -140,7 +133,7 @@
return errResponse;
}
- return functor(commandData, handler);
+ return functor(commandData, *handler);
}
} // namespace command