| #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; |
| } |