blob: 8009995acc40901980a021b903b18388f24a9a4f [file] [edit]
#include <helper/common.hpp>
#include <array>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
std::mutex m_mutex;
std::unordered_map<std::string, std::string> portToUdevIdMap;
void cleanupMctpAtExit()
{
portToUdevIdMap.clear();
}
/**
* @brief Execute a CLI command and return the command line result
*/
std::string exec(const char* cmd)
{
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, int (*)(FILE*)> pipe(popen(cmd, "r"), &pclose);
if (!pipe)
{
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
{
result += buffer.data();
}
return result;
}
std::optional<std::unordered_set<std::string>>
getActiveMctpPorts(std::string mctpLinkString)
{
size_t pos = 0;
std::string delimiter = "\n";
std::unordered_set<std::string> mctpPorts;
while ((pos = mctpLinkString.find(delimiter)) != std::string::npos)
{
std::string token = mctpLinkString.substr(0, pos);
size_t p = 0;
if ((p = token.find("mctpserial")) != std::string::npos)
{
// Take the first 11 characters from the output string
// That will be of format `mctpserial0` - 11 chars
mctpPorts.insert(token.substr(p, 11));
}
mctpLinkString = mctpLinkString.substr(pos + delimiter.length());
}
if (!mctpLinkString.empty())
{
size_t p;
if ((p = mctpLinkString.find("mctpserial")) != std::string::npos)
{
// Works only for 9 trays
mctpPorts.insert(mctpLinkString.substr(p, 11));
}
}
if (mctpPorts.empty())
{
return std::nullopt;
}
return mctpPorts;
}
/**
* @brief Extract the name from the command line result
* The result returns a string of the format
* "dev mctpserial0 index 13 address 0x(no-addr) net 7 mtu 68 up"
*
* To up link mctpserial0 for MCTP we need to extract the added "mctpserial0"
*
* @param[in] mctpLinkString - command line string returned after executing
* "mctp link show"
* @param[out] name - Name on which MCTP is to be set. e.g. "mctpserial0"
* @param[out] count - this is the index that is appended to the name, i.e "0"
* in "mctpserial0"
*
*/
std::optional<std::string> extractNewName(const std::string& preSetupOutput,
const std::string& postSetupOutput)
{
std::optional<std::unordered_set<std::string>> mctpPortsPreOpt =
getActiveMctpPorts(preSetupOutput);
std::unordered_set<std::string> preSetupPorts;
if (mctpPortsPreOpt.has_value())
{
preSetupPorts = mctpPortsPreOpt.value();
}
std::optional<std::unordered_set<std::string>> mctpPortsPostOpt =
getActiveMctpPorts(postSetupOutput);
if (mctpPortsPostOpt.has_value())
{
std::unordered_set<std::string> postSetupPorts =
mctpPortsPostOpt.value();
for (auto port : postSetupPorts)
{
auto it = preSetupPorts.find(port);
if (it == preSetupPorts.end())
{
return port;
}
}
}
std::cerr << "Link command failed\n";
return std::nullopt;
}
/**
* @brief Creating a unique network id from the udevid for a particular RDE
* device
*
* Currently this takes the udev id and adds the digits in it to create a net id
* For instance, for udev_id "1_1_4_1" the network id would be "7" (Sum of all
* digits)
*
* @param[in] udevid - Udev id of the device
*/
int createNetIdFromUdevId(std::string udevid)
{
// Creates a net id for a udev id by adding all the digits in the path
// 1_1_4_1 = 7
std::string delimiter = "_";
int netId = 0;
size_t pos = 0;
std::string token;
while ((pos = udevid.find(delimiter)) != std::string::npos)
{
token = udevid.substr(0, pos);
netId = netId + stoi(token);
udevid.erase(0, pos + delimiter.length());
}
if (!udevid.empty())
netId = netId + stoi(udevid);
return netId;
}
/**
* @brief kill all the mctp serial links produced after executing the cmd in
* paramcmd in param
*
* @param[in] - grepLinkCmd: Commnad for getting mctp serial links
*/
void killMctpSerialLinks(const std::string& grepLinkCmd)
{
std::string output = exec(grepLinkCmd.c_str());
std::vector<int> pids;
std::istringstream iss(output);
std::cerr << "Output for " << grepLinkCmd << ": \n" << output << "\n";
std::string line;
while (std::getline(iss, line))
{
// Create a stringstream from the input line
std::istringstream lineStream(line);
int pid;
if ((lineStream >> pid) && (line.find("grep") == std::string::npos) &&
(line.find("ps") == std::string::npos))
{
pids.emplace_back(pid);
}
}
for (const int& pid : pids)
{
std::string killCmd = std::format("kill -9 {}", pid);
std::cerr << "Killing existing mctp link"
<< " with " << killCmd << ".... Status: ";
exec(killCmd.c_str());
std::cerr << "Successfully completed" << std::endl;
}
if (pids.size() > 0)
{
// Sleep since unlinking might take a bit of time
sleep(2);
}
}
/**
* @brief Clean up existing mctp links for a particular device
*
* @param[in] - udevId: device id
*/
void cleanupMctpLink(const std::string& udevId)
{
std::cerr
<< "Checking if the mctp port exists after removal as a link...\n";
// check for the particular device if any mctp link present
auto it = portToUdevIdMap.find(udevId);
if (it != portToUdevIdMap.end())
{
std::string port = it->second;
std::cerr << "MCTP links serial removal executing for " << udevId
<< "\n";
std::string grepSerialLinks =
std::format("ps | grep \"mctp link serial {}\"", port);
killMctpSerialLinks(grepSerialLinks);
}
else
{
std::cerr << "No mctp link found to clean for udevId:" << udevId
<< "\n";
}
}
/**
* @brief Sets up MCTP on on "mctpserial" port
*
* **Currently this has some random sleeps attached as it takes some time to
* bring MCTP up. This function is only executed when a device reboots or the
* system boots**
*
* @param[in] port - the port at which MCTP is to be setup, format- "/dev/tty*"
* @param[in] udevid - udev id of the RDE device, which is a unique identifier
* and remains same for the device even after reboots
*/
int setupOnePort(const std::string& port, std::string udevid)
{
// TODO(b/284167548): Find an efficient way to set up MCTP without using CLI
// commands This might help us get rid of sleeps
// Mutex lock so that another device setup doesn't hinder the MCTP setup
m_mutex.lock();
bool setupComplete = false;
std::string mctpLinkShow = "mctp link show";
// lock mutex
int networkId;
for (int retryCounter = 0; retryCounter < MAX_RETRIES_MCTP_SOCK_FAILURE;
retryCounter++)
{
std::cerr << "Triggering MCTP setup on port: " << port
<< " with retry counter: " << retryCounter << "\n";
// Remove the existing port if running for the first time
if (portToUdevIdMap.size() == 0)
{
std::string grepAllMctpLinks = "ps | grep \"mctp link serial\"";
std::cerr << "Checking if any existing mctp links found...\n";
killMctpSerialLinks(grepAllMctpLinks);
}
// Executing link command pre setup to see current active ports
std::string linkResultPreSetup = exec(mctpLinkShow.c_str());
if (DEBUG)
{
std::cerr << "Executing linkResult pre setup: "
<< linkResultPreSetup << std::endl;
}
std::string linkCmd = "mctp link serial " + port + " &";
if (DEBUG)
{
std::cerr << "Executing: " << linkCmd << "\n";
}
int sysRc = system(linkCmd.c_str());
if (sysRc)
{
std::cerr << "Error in setting up mctp link with return code: "
<< std::to_string(sysRc) << "\n";
continue; // retry if link command fails
}
// Replace the new port in map
portToUdevIdMap.erase(udevid);
portToUdevIdMap.emplace(udevid, port);
// MCTP link command takes some time to finish - hence a sleep of 2s
sleep(2);
// Extract name:
std::string mctpName;
std::string linkResult = exec(mctpLinkShow.c_str());
if (DEBUG)
{
std::cerr << "Executing linkResult: " << linkResult << "\n";
}
std::optional<std::string> mctpNameOpt =
extractNewName(linkResultPreSetup, linkResult);
if (mctpNameOpt.has_value())
{
mctpName = mctpNameOpt.value();
}
else
{
std::cerr
<< "No mctp serial connection found. MCTP Link failed...\n";
continue;
}
networkId = createNetIdFromUdevId(udevid);
std::string upCmd = "mctp link set " + mctpName + " network " +
std::to_string(networkId) + " up";
if (DEBUG)
{
std::cerr << "Executing upcmd: " << upCmd << "\n";
}
std::string networkSetResponse = exec(upCmd.c_str());
if (networkSetResponse.find("invalid") != std::string::npos)
{
std::cerr << "Network setup failure... retrying\n";
continue;
}
// Setting up local addresses
std::string localAddrCmd = "mctp addr add 8 dev " + mctpName;
std::string routeCmd = "mctp route add 9 via " + mctpName;
if (DEBUG)
{
std::cerr << "Executing local addr: " << localAddrCmd << "\n";
}
exec(localAddrCmd.c_str());
if (DEBUG)
{
std::cerr << "Executing routeCmd: " << routeCmd << "\n";
}
exec(routeCmd.c_str());
setupComplete = true;
std::cout << "Setup completed for MCTP port: " << port << "\n";
break;
}
// unlock mutex
m_mutex.unlock();
if (setupComplete)
{
return networkId;
}
return -1;
}