Skip to content

Commit

Permalink
Broadcast Unknown-multicast and Unknown-unicast Storm-control (sonic-…
Browse files Browse the repository at this point in the history
…net#1306)

* Handle BUM Storm-control CONFIG_DB update.
* Segregate POLICER table and PORT_STORM_CONTROL table handling
* Broadcast, Unknown-multicast and Unknown-unicast storm-control on Ethernet interfaces.
  • Loading branch information
mohan-selvaraj authored and preetham-singh committed Aug 6, 2022
1 parent f039cab commit b373de8
Show file tree
Hide file tree
Showing 6 changed files with 621 additions and 8 deletions.
13 changes: 10 additions & 3 deletions orchagent/orchdaemon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ QosOrch *gQosOrch;
SwitchOrch *gSwitchOrch;
Directory<Orch*> gDirectory;
NatOrch *gNatOrch;
PolicerOrch *gPolicerOrch;
MlagOrch *gMlagOrch;
IsoGrpOrch *gIsoGrpOrch;
MACsecOrch *gMacsecOrch;
Expand Down Expand Up @@ -242,11 +243,17 @@ bool OrchDaemon::init()
};
gBufferOrch = new BufferOrch(m_applDb, m_configDb, m_stateDb, buffer_tables);

PolicerOrch *policer_orch = new PolicerOrch(m_configDb, "POLICER");
vector<TableConnector> policer_tables = {
TableConnector(m_configDb, CFG_POLICER_TABLE_NAME),
TableConnector(m_configDb, CFG_PORT_STORM_CONTROL_TABLE_NAME)
};

TableConnector stateDbStorm(m_stateDb, "BUM_STORM_CAPABILITY");
gPolicerOrch = new PolicerOrch(policer_tables, gPortsOrch);

TableConnector stateDbMirrorSession(m_stateDb, STATE_MIRROR_SESSION_TABLE_NAME);
TableConnector confDbMirrorSession(m_configDb, CFG_MIRROR_SESSION_TABLE_NAME);
gMirrorOrch = new MirrorOrch(stateDbMirrorSession, confDbMirrorSession, gPortsOrch, gRouteOrch, gNeighOrch, gFdbOrch, policer_orch);
gMirrorOrch = new MirrorOrch(stateDbMirrorSession, confDbMirrorSession, gPortsOrch, gRouteOrch, gNeighOrch, gFdbOrch, gPolicerOrch);

TableConnector confDbAclTable(m_configDb, CFG_ACL_TABLE_TABLE_NAME);
TableConnector confDbAclTableType(m_configDb, CFG_ACL_TABLE_TYPE_TABLE_NAME);
Expand Down Expand Up @@ -339,7 +346,7 @@ bool OrchDaemon::init()
* when iterating ConsumerMap. This is ensured implicitly by the order of keys in ordered map.
* For cases when Orch has to process tables in specific order, like PortsOrch during warm start, it has to override Orch::doTask()
*/
m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, gFlowCounterRouteOrch, mux_orch, mux_cb_orch, gIntfsOrch, gNeighOrch, gNhgMapOrch, gNhgOrch, gCbfNhgOrch, gRouteOrch, gCoppOrch, gQosOrch, wm_orch, policer_orch, tunnel_decap_orch, sflow_orch, gDebugCounterOrch, gMacsecOrch, gBfdOrch, gSrv6Orch};
m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, gFlowCounterRouteOrch, mux_orch, mux_cb_orch, gIntfsOrch, gNeighOrch, gNhgMapOrch, gNhgOrch, gCbfNhgOrch, gRouteOrch, gCoppOrch, gQosOrch, wm_orch, gPolicerOrch, tunnel_decap_orch, sflow_orch, gDebugCounterOrch, gMacsecOrch, gBfdOrch, gSrv6Orch};

bool initialize_dtel = false;
if (platform == BFN_PLATFORM_SUBSTRING || platform == VS_PLATFORM_SUBSTRING)
Expand Down
281 changes: 279 additions & 2 deletions orchagent/policerorch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ using namespace std;
using namespace swss;

extern sai_policer_api_t* sai_policer_api;
extern sai_port_api_t *sai_port_api;

extern sai_object_id_t gSwitchId;
extern PortsOrch* gPortsOrch;

#define ETHERNET_PREFIX "Ethernet"

static const string meter_type_field = "METER_TYPE";
static const string mode_field = "MODE";
static const string color_source_field = "COLOR_SOURCE";
Expand All @@ -23,6 +26,11 @@ static const string green_packet_action_field = "GREEN_PACKET_ACTION";
static const string red_packet_action_field = "RED_PACKET_ACTION";
static const string yellow_packet_action_field = "YELLOW_PACKET_ACTION";

static const string storm_control_kbps = "KBPS";
static const string storm_broadcast = "broadcast";
static const string storm_unknown_unicast = "unknown-unicast";
static const string storm_unknown_mcast = "unknown-multicast";

static const map<string, sai_meter_type_t> meter_type_map = {
{"PACKETS", SAI_METER_TYPE_PACKETS},
{"BYTES", SAI_METER_TYPE_BYTES}
Expand Down Expand Up @@ -105,15 +113,268 @@ bool PolicerOrch::decreaseRefCount(const string &name)
return true;
}

PolicerOrch::PolicerOrch(DBConnector* db, string tableName) :
Orch(db, tableName)
PolicerOrch::PolicerOrch(vector<TableConnector> &tableNames, PortsOrch *portOrch) : Orch(tableNames), m_portsOrch(portOrch)
{
SWSS_LOG_ENTER();
}

task_process_status PolicerOrch::handlePortStormControlTable(swss::KeyOpFieldsValuesTuple tuple)
{
auto key = kfvKey(tuple);
auto op = kfvOp(tuple);
string storm_key = key;
auto tokens = tokenize(storm_key, config_db_key_delimiter);
auto interface_name = tokens[0];
auto storm_type = tokens[1];
Port port;

/*Only proceed for Ethernet interfaces*/
if (strncmp(interface_name.c_str(), ETHERNET_PREFIX, strlen(ETHERNET_PREFIX)))
{
SWSS_LOG_ERROR("%s: Unsupported / Invalid interface %s",
storm_type.c_str(), interface_name.c_str());
return task_process_status::task_success;
}
if (!gPortsOrch->getPort(interface_name, port))
{
SWSS_LOG_ERROR("Failed to apply storm-control %s to port %s. Port not found",
storm_type.c_str(), interface_name.c_str());
/*continue here as there can be more interfaces*/
return task_process_status::task_success;
}
/*Policer Name: _<interface_name>_<storm_type>*/
const auto storm_policer_name = "_"+interface_name+"_"+storm_type;

if (op == SET_COMMAND)
{
// Mark the operation as an 'update', if the policer exists.
bool update = m_syncdPolicers.find(storm_policer_name) != m_syncdPolicers.end();
vector <sai_attribute_t> attrs;
bool cir = false;
sai_attribute_t attr;

/*Meter type hardcoded to BYTES*/
attr.id = SAI_POLICER_ATTR_METER_TYPE;
attr.value.s32 = (sai_meter_type_t) meter_type_map.at("BYTES");
attrs.push_back(attr);

/*Policer mode hardcoded to STORM_CONTROL*/
attr.id = SAI_POLICER_ATTR_MODE;
attr.value.s32 = (sai_policer_mode_t) policer_mode_map.at("STORM_CONTROL");
attrs.push_back(attr);

/*Red Packet Action hardcoded to DROP*/
attr.id = SAI_POLICER_ATTR_RED_PACKET_ACTION;
attr.value.s32 = packet_action_map.at("DROP");
attrs.push_back(attr);

for (auto i = kfvFieldsValues(tuple).begin();
i != kfvFieldsValues(tuple).end(); ++i)
{
auto field = to_upper(fvField(*i));
auto value = to_upper(fvValue(*i));

/*BPS value is used as CIR*/
if (field == storm_control_kbps)
{
attr.id = SAI_POLICER_ATTR_CIR;
/*convert kbps to bps*/
attr.value.u64 = (stoul(value)*1000/8);
cir = true;
attrs.push_back(attr);
SWSS_LOG_DEBUG("CIR %s",value.c_str());
}
else
{
SWSS_LOG_ERROR("Unknown storm control attribute %s specified",
field.c_str());
continue;
}
}
/*CIR is mandatory parameter*/
if (!cir)
{
SWSS_LOG_ERROR("Failed to create storm control policer %s,\
missing mandatory fields", storm_policer_name.c_str());
return task_process_status::task_failed;
}

/*Enabling storm-control on port*/
sai_attribute_t port_attr;
if (storm_type == storm_broadcast)
{
port_attr.id = SAI_PORT_ATTR_BROADCAST_STORM_CONTROL_POLICER_ID;
}
else if (storm_type == storm_unknown_unicast)
{
port_attr.id = SAI_PORT_ATTR_FLOOD_STORM_CONTROL_POLICER_ID;
}
else if (storm_type == storm_unknown_mcast)
{
port_attr.id = SAI_PORT_ATTR_MULTICAST_STORM_CONTROL_POLICER_ID;
}
else
{
SWSS_LOG_ERROR("Unknown storm_type %s", storm_type.c_str());
return task_process_status::task_failed;
}

sai_object_id_t policer_id;
// Create a new policer
if (!update)
{
sai_status_t status = sai_policer_api->create_policer(
&policer_id, gSwitchId, (uint32_t)attrs.size(), attrs.data());
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to create policer %s, rv:%d",
storm_policer_name.c_str(), status);
if (handleSaiCreateStatus(SAI_API_POLICER, status) == task_need_retry)
{
return task_process_status::task_need_retry;
}
}

SWSS_LOG_DEBUG("Created storm-control policer %s", storm_policer_name.c_str());
m_syncdPolicers[storm_policer_name] = policer_id;
m_policerRefCounts[storm_policer_name] = 0;
}
// Update an existing policer
else
{
policer_id = m_syncdPolicers[storm_policer_name];

// The update operation has limitations that it could only update
// the rate and the size accordingly.
// STORM_CONTROL: CIR, CBS
for (auto & attr: attrs)
{
if (attr.id != SAI_POLICER_ATTR_CIR)
{
continue;
}

sai_status_t status = sai_policer_api->set_policer_attribute(
policer_id, &attr);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to update policer %s attribute, rv:%d",
storm_policer_name.c_str(), status);
if (handleSaiSetStatus(SAI_API_POLICER, status) == task_need_retry)
{
return task_process_status::task_need_retry;
}

}
}
}
policer_id = m_syncdPolicers[storm_policer_name];

if (update)
{
SWSS_LOG_NOTICE("update storm-control policer %s", storm_policer_name.c_str());
port_attr.value.oid = SAI_NULL_OBJECT_ID;
/*Remove and re-apply policer*/
sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &port_attr);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to remove storm-control %s from port %s, rv:%d",
storm_type.c_str(), interface_name.c_str(), status);
if (handleSaiSetStatus(SAI_API_POLICER, status) == task_need_retry)
{
return task_process_status::task_need_retry;
}
}
}
port_attr.value.oid = policer_id;

sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &port_attr);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to apply storm-control %s to port %s, rv:%d",
storm_type.c_str(), interface_name.c_str(),status);

/*TODO: Do the below policer cleanup in an API*/
/*Remove the already created policer*/
if (SAI_STATUS_SUCCESS != sai_policer_api->remove_policer(
m_syncdPolicers[storm_policer_name]))
{
SWSS_LOG_ERROR("Failed to remove policer %s, rv:%d",
storm_policer_name.c_str(), status);
/*TODO: Just doing a syslog. */
}

SWSS_LOG_NOTICE("Removed policer %s as set_port_attribute for %s failed",
storm_policer_name.c_str(),interface_name.c_str());
m_syncdPolicers.erase(storm_policer_name);
m_policerRefCounts.erase(storm_policer_name);

return task_process_status::task_need_retry;
}
}
else if (op == DEL_COMMAND)
{
if (m_syncdPolicers.find(storm_policer_name) == m_syncdPolicers.end())
{
SWSS_LOG_ERROR("Policer %s not configured", storm_policer_name.c_str());
return task_process_status::task_success;
}

sai_attribute_t port_attr;
if (storm_type == storm_broadcast)
{
port_attr.id = SAI_PORT_ATTR_BROADCAST_STORM_CONTROL_POLICER_ID;
}
else if (storm_type == storm_unknown_unicast)
{
port_attr.id = SAI_PORT_ATTR_FLOOD_STORM_CONTROL_POLICER_ID;
}
else if (storm_type == storm_unknown_mcast)
{
port_attr.id = SAI_PORT_ATTR_MULTICAST_STORM_CONTROL_POLICER_ID;
}
else
{
SWSS_LOG_ERROR("Unknown storm_type %s", storm_type.c_str());
return task_process_status::task_failed;
}

port_attr.value.oid = SAI_NULL_OBJECT_ID;

sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &port_attr);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to remove storm-control %s from port %s, rv:%d",
storm_type.c_str(), interface_name.c_str(), status);
if (handleSaiRemoveStatus(SAI_API_POLICER, status) == task_need_retry)
{
return task_process_status::task_need_retry;
}
}

status = sai_policer_api->remove_policer(
m_syncdPolicers[storm_policer_name]);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to remove policer %s, rv:%d",
storm_policer_name.c_str(), status);
if (handleSaiRemoveStatus(SAI_API_POLICER, status) == task_need_retry)
{
return task_process_status::task_need_retry;
}
}

SWSS_LOG_NOTICE("Removed policer %s", storm_policer_name.c_str());
m_syncdPolicers.erase(storm_policer_name);
m_policerRefCounts.erase(storm_policer_name);
}
return task_process_status::task_success;
}

void PolicerOrch::doTask(Consumer &consumer)
{
SWSS_LOG_ENTER();
task_process_status storm_status = task_success;

if (!gPortsOrch->allPortsReady())
{
Expand All @@ -127,7 +388,23 @@ void PolicerOrch::doTask(Consumer &consumer)

auto key = kfvKey(tuple);
auto op = kfvOp(tuple);
auto table_name = consumer.getTableName();

// Special handling for storm-control configuration.
if (table_name == CFG_PORT_STORM_CONTROL_TABLE_NAME)
{
storm_status = handlePortStormControlTable(tuple);
if ((storm_status == task_process_status::task_success) ||
(storm_status == task_process_status::task_failed))
{
it = consumer.m_toSync.erase(it);
}
else
{
it++;
}
continue;
}
if (op == SET_COMMAND)
{
// Mark the operation as an 'update', if the policer exists.
Expand Down
6 changes: 5 additions & 1 deletion orchagent/policerorch.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@ typedef map<string, int> PolicerRefCountTable;
class PolicerOrch : public Orch
{
public:
PolicerOrch(DBConnector* db, string tableName);
PolicerOrch(vector<TableConnector> &tableNames, PortsOrch *portOrch);

bool policerExists(const string &name);
bool getPolicerOid(const string &name, sai_object_id_t &oid);

bool increaseRefCount(const string &name);
bool decreaseRefCount(const string &name);
task_process_status handlePortStormControlTable(swss::KeyOpFieldsValuesTuple tuple);
private:
PortsOrch *m_portsOrch;
virtual void doTask(Consumer& consumer);

PolicerTable m_syncdPolicers;
PolicerRefCountTable m_policerRefCounts;
};


7 changes: 6 additions & 1 deletion tests/mock_tests/aclorch_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,12 @@ namespace aclorch_test
};
gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch);

PolicerOrch *policer_orch = new PolicerOrch(m_config_db.get(), "POLICER");
vector<TableConnector> policer_tables = {
TableConnector(m_config_db.get(), CFG_POLICER_TABLE_NAME),
TableConnector(m_config_db.get(), CFG_PORT_STORM_CONTROL_TABLE_NAME)
};
TableConnector stateDbStorm(m_state_db.get(), "BUM_STORM_CAPABILITY");
PolicerOrch *policer_orch = new PolicerOrch(policer_tables, gPortsOrch);

TableConnector stateDbMirrorSession(m_state_db.get(), STATE_MIRROR_SESSION_TABLE_NAME);
TableConnector confDbMirrorSession(m_config_db.get(), CFG_MIRROR_SESSION_TABLE_NAME);
Expand Down
Loading

0 comments on commit b373de8

Please sign in to comment.