syncd/MdioIpcServer.cpp (403 lines of code) (raw):

#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <ctype.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/select.h> #include <sys/socket.h> #include <sys/un.h> #include <time.h> #include <fcntl.h> #include <unistd.h> #include "MdioIpcServer.h" #include "MdioIpcCommon.h" #include "meta/sai_serialize.h" #include "swss/logger.h" #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/trim.hpp> #include <vector> #ifndef COUNTOF #define COUNTOF(x) ((int)(sizeof((x)) / sizeof((x)[0]))) #endif using namespace syncd; bool MdioIpcServer::m_syncdContext = true; typedef struct syncd_mdio_ipc_conn_s { int fd; time_t timeout; } syncd_mdio_ipc_conn_t; MdioIpcServer::MdioIpcServer( _In_ std::shared_ptr<sairedis::SaiInterface> vendorSai, _In_ int globalContext): m_vendorSai(vendorSai), m_switchRid(SAI_NULL_OBJECT_ID), m_taskThread(), m_taskAlive(0) { SWSS_LOG_ENTER(); /* globalContext == 0 for syncd, globalContext > 0 for gbsyncd */ MdioIpcServer::m_syncdContext = (globalContext == 0); #ifdef MDIO_ACCESS_USE_NPU MdioIpcServer::m_accessUseNPU = true; #else MdioIpcServer::m_accessUseNPU = false; #endif } MdioIpcServer::~MdioIpcServer() { SWSS_LOG_ENTER(); m_taskAlive = 0; if(m_taskThread.joinable()) { m_taskThread.join(); } } void MdioIpcServer::setSwitchId( _In_ sai_object_id_t switchRid) { SWSS_LOG_ENTER(); /* Skip on any platform where MDIO access not using NPU */ if (!m_accessUseNPU) { return; } /* MDIO switch id is only relevant in syncd but not in gbsyncd */ if (!MdioIpcServer::m_syncdContext) { return; } if (m_switchRid != SAI_NULL_OBJECT_ID) { SWSS_LOG_ERROR("mdio switch id already initialized"); return; } m_switchRid = switchRid; SWSS_LOG_NOTICE("Initialize mdio switch id with RID = %s", sai_serialize_object_id(m_switchRid).c_str()); } void MdioIpcServer::setIpcTestMode() { SWSS_LOG_ENTER(); #ifndef MDIO_ACCESS_USE_NPU /* Allow unit test to start IPC server */ MdioIpcServer::m_accessUseNPU = true; #endif } /* * mdio <port_oid> reg: Read from the PHY register * mdio <port_oid> reg val: Write to the PHY register */ sai_status_t MdioIpcServer::syncd_ipc_cmd_mdio_common(char *resp, int argc, char *argv[]) { int ret = 0; uint32_t mdio_addr = 0, reg_addr = 0, val = 0; if (argc < 3) { return SAI_STATUS_INVALID_PARAMETER; } mdio_addr = (uint32_t)strtoul(argv[1], NULL, 0); reg_addr = (uint32_t)strtoul(argv[2], NULL, 0); if (m_switchRid == SAI_NULL_OBJECT_ID) { SWSS_LOG_ERROR("mdio switch id not initialized"); return SAI_STATUS_FAILURE; } if (argc > 3) { val = (uint32_t)strtoul(argv[3], NULL, 0); ret = m_vendorSai->switchMdioWrite(m_switchRid, mdio_addr, reg_addr, 1, &val); sprintf(resp, "%d\n", ret); } else { ret = m_vendorSai->switchMdioRead(m_switchRid, mdio_addr, reg_addr, 1, &val); sprintf(resp, "%d 0x%x\n", ret, val); } return (ret == 0) ? SAI_STATUS_SUCCESS : SAI_STATUS_FAILURE; } #if (SAI_API_VERSION >= SAI_VERSION(1, 11, 0)) sai_status_t MdioIpcServer::syncd_ipc_cmd_mdio_common_cl22(char *resp, int argc, char *argv[]) { int ret = 0; uint32_t mdio_addr = 0, reg_addr = 0, val = 0; if (argc < 3) { return SAI_STATUS_INVALID_PARAMETER; } mdio_addr = (uint32_t)strtoul(argv[1], NULL, 0); reg_addr = (uint32_t)strtoul(argv[2], NULL, 0); if (m_switchRid == SAI_NULL_OBJECT_ID) { SWSS_LOG_ERROR("mdio switch id not initialized"); return SAI_STATUS_FAILURE; } if (argc > 3) { val = (uint32_t)strtoul(argv[3], NULL, 0); ret = m_vendorSai->switchMdioCl22Write(m_switchRid, mdio_addr, reg_addr, 1, &val); sprintf(resp, "%d\n", ret); } else { ret = m_vendorSai->switchMdioCl22Read(m_switchRid, mdio_addr, reg_addr, 1, &val); sprintf(resp, "%d 0x%x\n", ret, val); } return (ret == 0) ? SAI_STATUS_SUCCESS : SAI_STATUS_FAILURE; } #endif sai_status_t MdioIpcServer::syncd_ipc_cmd_mdio(char *resp, int argc, char *argv[]) { return syncd_ipc_cmd_mdio_common(resp, argc, argv); } sai_status_t MdioIpcServer::syncd_ipc_cmd_mdio_cl22(char *resp, int argc, char *argv[]) { #if (SAI_API_VERSION >= SAI_VERSION(1, 11, 0)) return MdioIpcServer::syncd_ipc_cmd_mdio_common_cl22(resp, argc, argv); #else /* In this case, sai configuration should take care of mdio clause 22 */ return MdioIpcServer::syncd_ipc_cmd_mdio_common(resp, argc, argv); #endif } int MdioIpcServer::syncd_ipc_task_main() { SWSS_LOG_ENTER(); int i; int fd; int len; int ret; int sock_srv; int sock_cli; int sock_max; syncd_mdio_ipc_conn_t conn[MDIO_CONN_MAX]; struct sockaddr_un addr; char path[64]; fd_set rfds; char cmd[SYNCD_IPC_BUFF_SIZE], resp[SYNCD_IPC_BUFF_SIZE], *argv[64], *save; int argc = 0; strcpy(path, SYNCD_IPC_SOCK_SYNCD); fd = open(path, O_DIRECTORY); if (fd < 0) { SWSS_LOG_ERROR("Unable to open the directory %s for IPC\n", path); return errno; } sock_srv = socket(AF_UNIX, SOCK_STREAM, 0); if (sock_srv < 0) { SWSS_LOG_ERROR("socket() returns %d", errno); return errno; } /***************************************/ /* Set up the UNIX socket address */ /* by using AF_UNIX for the family and */ /* giving it a file path to bind to. */ /* */ /* Delete the file so the bind will */ /* succeed, then bind to that file. */ /***************************************/ memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; sprintf(addr.sun_path, "%s/%s.srv", path, SYNCD_IPC_SOCK_FILE); unlink(addr.sun_path); if (bind(sock_srv, (struct sockaddr *)&addr, sizeof(addr)) < 0) { SWSS_LOG_ERROR("bind() returns %d", errno); close(sock_srv); return errno; } /* Listen for the upcoming client sockets */ if (listen(sock_srv, MDIO_CONN_MAX) < 0) { SWSS_LOG_ERROR("listen() returns %d", errno); unlink(addr.sun_path); close(sock_srv); return errno; } SWSS_LOG_NOTICE("IPC service is online\n"); memset(conn, 0, sizeof(conn)); while (m_taskAlive) { time_t now; struct timeval timeout; /* garbage collection */ now = time(NULL); for (i = 0; i < MDIO_CONN_MAX; ++i) { if ((conn[i].fd > 0) && (conn[i].timeout < now)) { SWSS_LOG_NOTICE("socket %d: connection timeout\n", conn[i].fd); close(conn[i].fd); conn[i].fd = 0; conn[i].timeout = 0; } } /* reset read file descriptors */ FD_ZERO(&rfds); FD_SET(sock_srv, &rfds); sock_max = sock_srv; for (i = 0; i < MDIO_CONN_MAX; ++i) { if (conn[i].fd <= 0) { continue; } FD_SET(conn[i].fd, &rfds); if (sock_max < conn[i].fd) { sock_max = conn[i].fd; } } /* monitor the socket descriptors */ timeout.tv_sec = 1; timeout.tv_usec = 0; ret = select(sock_max + 1, &rfds, NULL, NULL, &timeout); if (ret == 0) { continue; } else if (ret < 0) { if (errno == EINTR) { continue; } else { SWSS_LOG_ERROR("select() returns %d", errno); break; } } /* Accept the new connection */ now = time(NULL); if (FD_ISSET(sock_srv, &rfds)) { sock_cli = accept(sock_srv, NULL, NULL); if (sock_cli <= 0) { SWSS_LOG_ERROR("accept() returns %d", errno); continue; } for (i = 0; i < MDIO_CONN_MAX; ++i) { if (conn[i].fd <= 0) { break; } } if (i < MDIO_CONN_MAX) { conn[i].fd = sock_cli; conn[i].timeout = now + MDIO_SERVER_TIMEOUT; } else { SWSS_LOG_ERROR("too many connections!"); close(sock_cli); } } /* Handle the client requests */ for (i = 0; i < MDIO_CONN_MAX; ++i) { sai_status_t rc = SAI_STATUS_NOT_SUPPORTED; sock_cli = conn[i].fd; if ((sock_cli <= 0) || !FD_ISSET(sock_cli, &rfds)) { continue; } /* get the command message */ len = (int)recv(sock_cli, (void *)cmd, sizeof(cmd) - 1, 0); if (len <= 0) { close(sock_cli); conn[i].fd = 0; conn[i].timeout = 0; continue; } cmd[len] = 0; /* tokenize the command string */ argc = 0; std::string str(cmd); boost::algorithm::trim(str); boost::algorithm::to_lower(str); std::vector<char>v(str.size()+1); memcpy( &v.front(), str.c_str(), str.size() + 1 ); argv[argc++] = strtok_r(v.data(), " \t\r\n", &save); while (argc < COUNTOF(argv)) { argv[argc] = strtok_r(NULL, " \t\r\n", &save); if (argv[argc] == NULL) break; ++argc; } /* command dispatch */ resp[0] = 0; rc = SAI_STATUS_NOT_SUPPORTED; if (argv[0] == NULL) { rc = SAI_STATUS_NOT_SUPPORTED; } else if (strcmp("mdio", argv[0]) == 0) { rc = MdioIpcServer::syncd_ipc_cmd_mdio(resp, argc, argv); if (rc != 0) { SWSS_LOG_ERROR("command %s returns %d", cmd, rc); } } else if (strcmp("mdio-cl22", argv[0]) == 0) { rc = MdioIpcServer::syncd_ipc_cmd_mdio_cl22(resp, argc, argv); if (rc != 0) { SWSS_LOG_ERROR("command %s returns %d", cmd, rc); } } /* build the error message */ if (rc != SAI_STATUS_SUCCESS) { sprintf(resp, "%d\n", rc); } /* send out the response */ len = (int)strlen(resp); if (send(sock_cli, resp, len, 0) < len) { SWSS_LOG_ERROR("send() returns %d", errno); } /* update the connection timeout counter */ conn[i].timeout = time(NULL) + MDIO_SERVER_TIMEOUT; } } /* close socket descriptors */ for (i = 0; i < MDIO_CONN_MAX; ++i) { if (conn[i].fd <= 0) { continue; } close(conn[i].fd); } close(sock_srv); unlink(addr.sun_path); return errno; } void MdioIpcServer::syncd_ipc_task_enter(void *ctx) { SWSS_LOG_ENTER(); MdioIpcServer *mdioServer = (MdioIpcServer *)ctx; mdioServer->syncd_ipc_task_main(); } void MdioIpcServer::stopMdioThread(void) { SWSS_LOG_ENTER(); /* Skip on any platform where MDIO access not using NPU */ if (!m_accessUseNPU) { return; } /* MDIO IPC server thread is only relevant in syncd but not in gbsyncd */ if (!MdioIpcServer::m_syncdContext) { return; } m_taskAlive = 0; if(m_taskThread.joinable()) { m_taskThread.join(); } SWSS_LOG_NOTICE("IPC task thread is stopped\n"); } int MdioIpcServer::startMdioThread() { SWSS_LOG_ENTER(); /* Skip on any platform where MDIO access not using NPU */ if (!m_accessUseNPU) { return SAI_STATUS_SUCCESS; } /* MDIO IPC server thread is only relevant in syncd but not in gbsyncd */ if (!MdioIpcServer::m_syncdContext) { return 0; } if (!m_taskAlive) { m_taskAlive = 1; try { m_taskThread = std::thread(&MdioIpcServer::syncd_ipc_task_enter, this); } catch(const std::exception& e) { MdioIpcServer::m_taskAlive = 0; SWSS_LOG_ERROR("Unable to create IPC task thread: %s", e.what()); return SAI_STATUS_FAILURE; } } return SAI_STATUS_SUCCESS; }