Skip to content

Commit

Permalink
[portsorch]: Add support of cable breakout feature (sonic-net#320)
Browse files Browse the repository at this point in the history
Signed-off-by: Volodymyr Samotiy <[email protected]>
  • Loading branch information
Volodymyr Samotiy authored and Shuotian Cheng committed Oct 3, 2017
1 parent bcdea13 commit 53d6a1d
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 43 deletions.
186 changes: 153 additions & 33 deletions orchagent/portsorch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
#include <sstream>
#include <set>
#include <algorithm>
#include <tuple>

#include <netinet/if_ether.h>
#include "net/if.h"

#include "logger.h"
#include "schema.h"
#include "converter.h"

extern sai_switch_api_t *sai_switch_api;
extern sai_bridge_api_t *sai_bridge_api;
Expand Down Expand Up @@ -470,6 +472,107 @@ void PortsOrch::updateDbPortOperStatus(sai_object_id_t id, sai_port_oper_status_
}
}

bool PortsOrch::addPort(const set<int> &lane_set, uint32_t speed)
{
SWSS_LOG_ENTER();

vector<uint32_t> lanes(lane_set.begin(), lane_set.end());

sai_attribute_t attr;
vector<sai_attribute_t> attrs;

attr.id = SAI_PORT_ATTR_SPEED;
attr.value.u32 = speed;
attrs.push_back(attr);

attr.id = SAI_PORT_ATTR_HW_LANE_LIST;
attr.value.u32list.list = lanes.data();
attr.value.u32list.count = static_cast<uint32_t>(lanes.size());
attrs.push_back(attr);

sai_object_id_t port_id;
sai_status_t status = sai_port_api->create_port(&port_id, gSwitchId, static_cast<uint32_t>(attrs.size()), attrs.data());
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to create port with the speed %u, rv:%d", speed, status);
return false;
}

m_portListLaneMap[lane_set] = port_id;

SWSS_LOG_NOTICE("Create port %lx with the speed %u", port_id, speed);

return true;
}

bool PortsOrch::removePort(sai_object_id_t port_id)
{
SWSS_LOG_ENTER();

sai_status_t status = sai_port_api->remove_port(port_id);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to remove port %lx, rv:%d", port_id, status);
return false;
}

SWSS_LOG_NOTICE("Remove port %lx", port_id);

return true;
}

bool PortsOrch::initPort(const string &alias, const set<int> &lane_set)
{
SWSS_LOG_ENTER();

/* Determine if the lane combination exists in switch */
if (m_portListLaneMap.find(lane_set) != m_portListLaneMap.end())
{
sai_object_id_t id = m_portListLaneMap[lane_set];

/* Determine if the port has already been initialized before */
if (m_portList.find(alias) != m_portList.end() && m_portList[alias].m_port_id == id)
{
SWSS_LOG_INFO("Port has already been initialized before alias:%s", alias.c_str());
}
else
{
Port p(alias, Port::PHY);

p.m_index = static_cast<int32_t>(m_portList.size()); // TODO: Assume no deletion of physical port
p.m_port_id = id;

/* Initialize the port and create router interface and host interface */
if (initializePort(p))
{
/* Add port to port list */
m_portList[alias] = p;
/* Add port name map to counter table */
std::stringstream ss;
ss << hex << p.m_port_id;
FieldValueTuple tuple(p.m_alias, ss.str());
vector<FieldValueTuple> vector;
vector.push_back(tuple);
m_counterTable->set("", vector);

SWSS_LOG_NOTICE("Initialized port %s", alias.c_str());
}
else
{
SWSS_LOG_ERROR("Failed to initialize port %s", alias.c_str());
return false;
}
}
}
else
{
SWSS_LOG_ERROR("Failed to locate port lane combination alias:%s", alias.c_str());
return false;
}

return true;
}

void PortsOrch::doPortTask(Consumer &consumer)
{
SWSS_LOG_ENTER();
Expand All @@ -482,21 +585,34 @@ void PortsOrch::doPortTask(Consumer &consumer)
string alias = kfvKey(t);
string op = kfvOp(t);

if (alias == "PortConfigDone")
{
m_portConfigDone = true;

for (auto i : kfvFieldsValues(t))
{
if (fvField(i) == "count")
{
m_portCount = to_uint<uint32_t>(fvValue(i));
}
}
}

/* Get notification from application */
/* portsyncd application:
* When portsorch receives 'ConfigDone' message, it indicates port initialization
* When portsorch receives 'PortInitDone' message, it indicates port initialization
* procedure is done. Before port initialization procedure, none of other tasks
* are executed.
*/
if (alias == "ConfigDone")
if (alias == "PortInitDone")
{
/* portsyncd restarting case:
* When portsyncd restarts, duplicate notifications may be received.
*/
if (!m_initDone)
{
m_initDone = true;
SWSS_LOG_INFO("Get ConfigDone notification from portsyncd.");
SWSS_LOG_INFO("Get PortInitDone notification from portsyncd.");
}

it = consumer.m_toSync.erase(it);
Expand All @@ -523,7 +639,6 @@ void PortsOrch::doPortTask(Consumer &consumer)
int lane = stoi(lane_str);
lane_set.insert(lane);
}

}

/* Set port admin status */
Expand All @@ -539,47 +654,52 @@ void PortsOrch::doPortTask(Consumer &consumer)
speed = (uint32_t)stoul(fvValue(i));
}

/* Collect information about all received ports */
if (lane_set.size())
{
/* Determine if the lane combination exists in switch */
if (m_portListLaneMap.find(lane_set) !=
m_portListLaneMap.end())
{
sai_object_id_t id = m_portListLaneMap[lane_set];
m_lanesAliasSpeedMap[lane_set] = make_tuple(alias, speed);
}

/* Determin if the port has already been initialized before */
if (m_portList.find(alias) != m_portList.end() && m_portList[alias].m_port_id == id)
/* Once all ports received, go through the each port and perform appropriate actions:
* 1. Remove ports which don't exist anymore
* 2. Create new ports
* 3. Initialize all ports
*/
if (m_portConfigDone && (m_lanesAliasSpeedMap.size() == m_portCount))
{
for (auto it = m_portListLaneMap.begin(); it != m_portListLaneMap.end();)
{
if (m_lanesAliasSpeedMap.find(it->first) == m_lanesAliasSpeedMap.end())
{
SWSS_LOG_INFO("Port has already been initialized before alias:%s", alias.c_str());
if (!removePort(it->second))
{
throw runtime_error("PortsOrch initialization failure.");
}
it = m_portListLaneMap.erase(it);
}
else
{
Port p(alias, Port::PHY);

p.m_index = (uint32_t)m_portList.size(); // TODO: Assume no deletion of physical port
p.m_port_id = id;
++it;
}
}

/* Initialize the port and create router interface and host interface */
if (initializePort(p))
for (auto it = m_lanesAliasSpeedMap.begin(); it != m_lanesAliasSpeedMap.end();)
{
if (m_portListLaneMap.find(it->first) == m_portListLaneMap.end())
{
if (!addPort(it->first, get<1>(it->second)))
{
/* Add port to port list */
m_portList[alias] = p;
/* Add port name map to counter table */
std::stringstream ss;
ss << hex << p.m_port_id;
FieldValueTuple tuple(p.m_alias, ss.str());
vector<FieldValueTuple> vector;
vector.push_back(tuple);
m_counterTable->set("", vector);

SWSS_LOG_NOTICE("Initialized port %s", alias.c_str());
throw runtime_error("PortsOrch initialization failure.");
}
else
SWSS_LOG_ERROR("Failed to initialize port %s", alias.c_str());
}

if (!initPort(get<0>(it->second), it->first))
{
throw runtime_error("PortsOrch initialization failure.");
}

it = m_lanesAliasSpeedMap.erase(it);
}
else
SWSS_LOG_ERROR("Failed to locate port lane combination alias:%s", alias.c_str());
}

Port p;
Expand Down
6 changes: 6 additions & 0 deletions orchagent/portsorch.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ class PortsOrch : public Orch, public Subject
sai_object_id_t m_default1QBridge;
sai_object_id_t m_defaultVlan;

bool m_portConfigDone = false;
sai_uint32_t m_portCount;
map<set<int>, sai_object_id_t> m_portListLaneMap;
map<set<int>, tuple<string, uint32_t>> m_lanesAliasSpeedMap;
map<string, Port> m_portList;

void doTask(Consumer &consumer);
Expand Down Expand Up @@ -100,6 +102,10 @@ class PortsOrch : public Orch, public Subject
bool addLagMember(Port lag, Port port);
bool removeLagMember(Port lag, Port port);

bool addPort(const set<int> &lane_set, uint32_t speed);
bool removePort(sai_object_id_t port_id);
bool initPort(const string &alias, const set<int> &lane_set);

bool setPortAdminStatus(sai_object_id_t id, bool up);
bool setPortMtu(sai_object_id_t id, sai_uint32_t mtu);

Expand Down
57 changes: 47 additions & 10 deletions portsyncd/portsyncd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <vector>
#include <set>
#include <map>
#include <list>
#include "dbconnector.h"
#include "select.h"
#include "netdispatcher.h"
Expand All @@ -24,7 +25,7 @@ using namespace swss;
* interfaces are already created and remove them from this set. We will
* remove the rest of the ports in the set when receiving the first netlink
* message indicating that the host interfaces are created. After the set
* is empty, we send out the signal ConfigDone. g_init is used to limit the
* is empty, we send out the signal PortInitDone. g_init is used to limit the
* command to be run only once.
*/
set<string> g_portSet;
Expand Down Expand Up @@ -107,7 +108,7 @@ int main(int argc, char **argv)
*/
FieldValueTuple finish_notice("lanes", "0");
vector<FieldValueTuple> attrs = { finish_notice };
p.set("ConfigDone", attrs);
p.set("PortInitDone", attrs);

g_init = true;
}
Expand All @@ -134,34 +135,70 @@ void handlePortConfigFile(ProducerStateTable &p, string file)
throw "Port configuration file not found!";
}

list<string> header = {"name", "lanes", "alias", "speed"};
string line;
while (getline(infile, line))
{
if (line.at(0) == '#')
{
/* Find out what info is specified in the configuration file */
for (auto it = header.begin(); it != header.end();)
{
if (line.find(*it) == string::npos)
{
it = header.erase(it);
}
else
{
++it;
}
}

continue;
}

istringstream iss(line);
string name, lanes, alias;
iss >> name >> lanes >> alias;
map<string, string> entry;

/* If port has no alias, then use its' name as alias */
if (alias == "")
/* Read port configuration entry */
for (auto column : header)
{
alias = name;
iss >> entry[column];
}
FieldValueTuple lanes_attr("lanes", lanes);

/* If port has no alias, then use its name as alias */
string alias;
if ((entry.find("alias") != entry.end()) && (entry["alias"] != ""))
{
alias = entry["alias"];
}
else
{
alias = entry["name"];
}

FieldValueTuple lanes_attr("lanes", entry["lanes"]);
FieldValueTuple alias_attr("alias", alias);

vector<FieldValueTuple> attrs;
attrs.push_back(lanes_attr);
attrs.push_back(alias_attr);

p.set(name, attrs);
if ((entry.find("speed") != entry.end()) && (entry["speed"] != ""))
{
FieldValueTuple speed_attr("speed", entry["speed"]);
attrs.push_back(speed_attr);
}

g_portSet.insert(name);
p.set(entry["name"], attrs);

g_portSet.insert(entry["name"]);
}

infile.close();

/* Notify that all ports added */
FieldValueTuple finish_notice("count", to_string(g_portSet.size()));
vector<FieldValueTuple> attrs = { finish_notice };
p.set("PortConfigDone", attrs);
}

0 comments on commit 53d6a1d

Please sign in to comment.