diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index c227ee71d02..84f2d7a5572 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -10,7 +10,7 @@ else DBGFLAGS = -g endif -orchagent_SOURCES = main.cpp orchdaemon.cpp orch.cpp routeorch.cpp neighorch.cpp intfsorch.cpp portsorch.cpp +orchagent_SOURCES = main.cpp orchdaemon.cpp orch.cpp routeorch.cpp neighorch.cpp intfsorch.cpp portsorch.cpp qosorch.cpp bufferorch.cpp orchagent_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) orchagent_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) diff --git a/orchagent/bufferorch.cpp b/orchagent/bufferorch.cpp new file mode 100644 index 00000000000..690b1c14399 --- /dev/null +++ b/orchagent/bufferorch.cpp @@ -0,0 +1,629 @@ +#include "sai.h" + +#include "bufferorch.h" +#include "logger.h" + +#include +#include + +extern sai_port_api_t *sai_port_api; +extern sai_queue_api_t *sai_queue_api; +extern sai_switch_api_t *sai_switch_api; +extern sai_buffer_api_t *sai_buffer_api; + +type_map BufferOrch::m_buffer_type_maps = { + {APP_BUFFER_POOL_TABLE_NAME, new object_map()}, + {APP_BUFFER_PROFILE_TABLE_NAME, new object_map()}, + {APP_BUFFER_QUEUE_TABLE_NAME, new object_map()}, + {APP_BUFFER_PG_TABLE_NAME, new object_map()}, + {APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, new object_map()}, + {APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME, new object_map()} +}; + +BufferOrch::BufferOrch(DBConnector *db, vector &tableNames, PortsOrch *portsOrch) : + Orch(db, tableNames), m_portsOrch(portsOrch) +{ + SWSS_LOG_ENTER(); + initTableHandlers(); +}; + +void BufferOrch::initTableHandlers() +{ + SWSS_LOG_ENTER(); + m_bufferHandlerMap.insert(buffer_handler_pair(APP_BUFFER_POOL_TABLE_NAME, &BufferOrch::processBufferPool)); + m_bufferHandlerMap.insert(buffer_handler_pair(APP_BUFFER_PROFILE_TABLE_NAME, &BufferOrch::processBufferProfile)); + m_bufferHandlerMap.insert(buffer_handler_pair(APP_BUFFER_QUEUE_TABLE_NAME, &BufferOrch::processQueue)); + m_bufferHandlerMap.insert(buffer_handler_pair(APP_BUFFER_PG_TABLE_NAME, &BufferOrch::processPriorityGroup)); + m_bufferHandlerMap.insert(buffer_handler_pair(APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, &BufferOrch::processIngressBufferProfileList)); + m_bufferHandlerMap.insert(buffer_handler_pair(APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME, &BufferOrch::processEgressBufferProfileList)); +} + +task_process_status BufferOrch::processBufferPool(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + sai_status_t sai_status; + sai_object_id_t sai_object = SAI_NULL_OBJECT_ID; + auto it = consumer.m_toSync.begin(); + KeyOpFieldsValuesTuple tuple = it->second; + string map_type_name = consumer.m_consumer->getTableName(); + string object_name = kfvKey(tuple); + string op = kfvOp(tuple); + + SWSS_LOG_DEBUG("object name:%s", object_name.c_str()); + if (map_type_name != APP_BUFFER_POOL_TABLE_NAME) + { + SWSS_LOG_ERROR("Key with invalid table type passed in %s, expected:%s\n", map_type_name.c_str(), APP_BUFFER_POOL_TABLE_NAME); + return task_process_status::task_invalid_entry; + } + if (m_buffer_type_maps[map_type_name]->find(object_name) != m_buffer_type_maps[map_type_name]->end()) + { + sai_object = (*(m_buffer_type_maps[map_type_name]))[object_name]; + SWSS_LOG_DEBUG("found existing object:%s of type:%s", object_name.c_str(), map_type_name.c_str()); + } + SWSS_LOG_DEBUG("processing command:%s", op.c_str()); + if (op == SET_COMMAND) + { + std::vector attribs; + for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++) + { + SWSS_LOG_DEBUG("field:%s, value:%s", fvField(*i).c_str(), fvValue(*i).c_str()); + sai_attribute_t attr; + if (fvField(*i) == buffer_size_field_name) + { + attr.id = SAI_BUFFER_POOL_ATTR_SIZE; + attr.value.u32 = std::stoul(fvValue(*i)); + attribs.push_back(attr); + } + else if (fvField(*i) == buffer_pool_type_field_name) + { + string type = fvValue(*i); + if (type == buffer_value_ingress) + { + attr.value.u32 = SAI_BUFFER_POOL_INGRESS; + } + else if (type == buffer_value_egress) + { + attr.value.u32 = SAI_BUFFER_POOL_EGRESS; + } + else + { + SWSS_LOG_ERROR("Unknown pool type specified:%s\n", type.c_str()); + return task_process_status::task_invalid_entry; + } + attr.id = SAI_BUFFER_POOL_ATTR_TYPE; + attribs.push_back(attr); + } + else if (fvField(*i) == buffer_pool_mode_field_name) + { + string mode = fvValue(*i); + if (mode == buffer_pool_mode_dynamic_value) + { + attr.value.u32 = SAI_BUFFER_THRESHOLD_MODE_DYNAMIC; + } + else if (mode == buffer_pool_mode_static_value) + { + attr.value.u32 = SAI_BUFFER_THRESHOLD_MODE_STATIC; + } + else + { + SWSS_LOG_ERROR("Unknown pool mode specified:%s\n", mode.c_str()); + return task_process_status::task_invalid_entry; + } + attr.id = SAI_BUFFER_POOL_ATTR_TH_MODE; + attribs.push_back(attr); + } + else + { + SWSS_LOG_ERROR("Unknown pool field specified:%s, ignoring\n", fvField(*i).c_str()); + continue; + } + } + if (SAI_NULL_OBJECT_ID != sai_object) + { + SWSS_LOG_DEBUG("Modifying existing sai object:%llx ", sai_object); + sai_status = sai_buffer_api->set_buffer_pool_attr(sai_object, &attribs[0]); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to modify buffer pool, name:%s, sai object:%llx, status:%d", object_name.c_str(), sai_object, sai_status); + return task_process_status::task_failed; + } + SWSS_LOG_DEBUG("Modified existing pool:%llx, type:%s name:%s ", sai_object, map_type_name.c_str(), object_name.c_str()); + } + else + { + SWSS_LOG_DEBUG("Creating new sai object"); + sai_status = sai_buffer_api->create_buffer_pool(&sai_object, attribs.size(), attribs.data()); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to create buffer pool, name:%s, status:%d", object_name.c_str(), sai_status); + return task_process_status::task_failed; + } + (*(m_buffer_type_maps[map_type_name]))[object_name] = sai_object; + SWSS_LOG_DEBUG("Created new pool:%llx, type:%s name:%s ", sai_object, map_type_name.c_str(), object_name.c_str()); + } + } + else if (op == DEL_COMMAND) + { + sai_status = sai_buffer_api->remove_buffer_pool(sai_object); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to remove buffer pool, name:%s, sai object:%llx, status:%d", object_name.c_str(), sai_object, sai_status); + return task_process_status::task_failed; + } + auto it_to_delete = (m_buffer_type_maps[map_type_name])->find(object_name); + (m_buffer_type_maps[map_type_name])->erase(it_to_delete); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + return task_process_status::task_invalid_entry; + } + return task_process_status::task_success; +} + +task_process_status BufferOrch::processBufferProfile(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + sai_status_t sai_status; + sai_object_id_t sai_object = SAI_NULL_OBJECT_ID; + auto it = consumer.m_toSync.begin(); + KeyOpFieldsValuesTuple tuple = it->second; + string map_type_name = consumer.m_consumer->getTableName(); + string object_name = kfvKey(tuple); + string op = kfvOp(tuple); + + SWSS_LOG_DEBUG("object name:%s", object_name.c_str()); + if (map_type_name != APP_BUFFER_PROFILE_TABLE_NAME) + { + SWSS_LOG_ERROR("Key with invalid table type passed in %s, expected:%s\n", map_type_name.c_str(), APP_BUFFER_PROFILE_TABLE_NAME); + return task_process_status::task_invalid_entry; + } + if (m_buffer_type_maps[map_type_name]->find(object_name) != m_buffer_type_maps[map_type_name]->end()) + { + sai_object = (*(m_buffer_type_maps[map_type_name]))[object_name]; + SWSS_LOG_DEBUG("found existing object:%s of type:%s", object_name.c_str(), map_type_name.c_str()); + } + SWSS_LOG_DEBUG("processing command:%s", op.c_str()); + if (op == SET_COMMAND) + { + std::vector attribs; + for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++) + { + SWSS_LOG_DEBUG("field:%s, value:%s", fvField(*i).c_str(), fvValue(*i).c_str()); + sai_attribute_t attr; + if (fvField(*i) == buffer_pool_field_name) + { + sai_object_id_t sai_pool; + ref_resolve_status resolve_result = resolveFieldRefValue(m_buffer_type_maps, buffer_pool_field_name, tuple, sai_pool); + if (ref_resolve_status::success != resolve_result) + { + SWSS_LOG_ERROR("Missing or invalid pool reference specified\n"); + return task_process_status::task_need_retry; + } + attr.id = SAI_BUFFER_PROFILE_ATTR_POOL_ID; + attr.value.oid = sai_pool; + attribs.push_back(attr); + } + else if (fvField(*i) == buffer_xon_field_name) + { + attr.value.u32 = std::stoul(fvValue(*i)); + attr.id = SAI_BUFFER_PROFILE_ATTR_XON_TH; + attribs.push_back(attr); + } + else if (fvField(*i) == buffer_xoff_field_name) + { + attr.value.u32 = std::stoul(fvValue(*i)); + attr.id = SAI_BUFFER_PROFILE_ATTR_XOFF_TH; + attribs.push_back(attr); + } + else if (fvField(*i) == buffer_size_field_name) + { + attr.id = SAI_BUFFER_PROFILE_ATTR_BUFFER_SIZE; + attr.value.u32 = std::stoul(fvValue(*i)); + attribs.push_back(attr); + } + else if (fvField(*i) == buffer_dynamic_th_field_name) + { + attr.id = SAI_BUFFER_PROFILE_ATTR_SHARED_DYNAMIC_TH; + attr.value.u32 = std::stoul(fvValue(*i)); + attribs.push_back(attr); + } + else if (fvField(*i) == buffer_static_th_field_name) + { + attr.id = SAI_BUFFER_PROFILE_ATTR_SHARED_STATIC_TH; + attr.value.u32 = std::stoul(fvValue(*i)); + attribs.push_back(attr); + } + else + { + SWSS_LOG_ERROR("Unknown buffer profile field specified:%s, ignoring\n", fvField(*i).c_str()); + continue; + } + } + if (SAI_NULL_OBJECT_ID != sai_object) + { + SWSS_LOG_DEBUG("Modifying existing sai object:%llx ", sai_object); + sai_status = sai_buffer_api->set_buffer_profile_attr(sai_object, &attribs[0]); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to modify buffer profile, name:%s, sai object:%llx, status:%d", object_name.c_str(), sai_object, sai_status); + return task_process_status::task_failed; + } + } + else + { + SWSS_LOG_DEBUG("Creating new sai object"); + sai_status = sai_buffer_api->create_buffer_profile(&sai_object, attribs.size(), attribs.data()); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to create buffer profile, name:%s, status:%d", object_name.c_str(), sai_status); + return task_process_status::task_failed; + } + (*(m_buffer_type_maps[map_type_name]))[object_name] = sai_object; + SWSS_LOG_DEBUG("Created new sai object:%llx, type:%s name:%s ", sai_object, map_type_name.c_str(), object_name.c_str()); + } + } + else if (op == DEL_COMMAND) + { + sai_status = sai_buffer_api->remove_buffer_profile(sai_object); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to remove buffer profile, name:%s, sai object:%llx, status:%d", object_name.c_str(), sai_object, sai_status); + return task_process_status::task_failed; + } + auto it_to_delete = (m_buffer_type_maps[map_type_name])->find(object_name); + (m_buffer_type_maps[map_type_name])->erase(it_to_delete); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + return task_process_status::task_invalid_entry; + } + return task_process_status::task_success; +} + +bool BufferOrch::parseIndexRange(const string &input, sai_uint32_t &range_low, sai_uint32_t &range_high) +{ + SWSS_LOG_ENTER(); + SWSS_LOG_DEBUG("input:%s", input.c_str()); + if (input.find(range_specifier) != string::npos) + { + vector range_values; + if (!tokenizeString(input, range_specifier, range_values)) + { + SWSS_LOG_ERROR("Failed to parse index in:%s\n", input.c_str()); + return false; + } + if (range_values.size() != 2) + { + SWSS_LOG_ERROR("malformed index range in:%s. Must contain 2 tokens\n", input.c_str()); + return false; + } + range_low = std::stoul(range_values[0]); + range_high = std::stoul(range_values[1]); + if (range_low >= range_high) + { + SWSS_LOG_ERROR("malformed index range in:%s. left value must be less than righ value.\n", input.c_str()); + return false; + } + } + else + { + range_low = range_high = std::stoul(input); + } + SWSS_LOG_DEBUG("resulting range:%d-%d", range_low, range_high); + return true; +} + +bool BufferOrch::parseNameArray(const string &input, vector &port_names) +{ + if (input.find(comma) == string::npos) + { + port_names.push_back(input); + return true; + } + return tokenizeString(input, list_item_delimiter, port_names); +} + +/* +Input sample "BUFFER_QUEUE_TABLE:Ethernet4,Ethernet45:10-15" +*/ +task_process_status BufferOrch::processQueue(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + auto it = consumer.m_toSync.begin(); + KeyOpFieldsValuesTuple tuple = it->second; + sai_object_id_t sai_buffer_profile; + string key = kfvKey(tuple); + string op = kfvOp(tuple); + vector tokens; + sai_uint32_t range_low, range_high; + + SWSS_LOG_DEBUG("Processing:%s", key.c_str()); + if (!tokenizeString(key, delimiter, tokens)) + { + return task_process_status::task_invalid_entry; + } + if (tokens.size() != 2) + { + SWSS_LOG_ERROR("malformed key:%s. Must contain 2 tokens\n", key.c_str()); + return task_process_status::task_invalid_entry; + } + if (consumer.m_consumer->getTableName() != APP_BUFFER_QUEUE_TABLE_NAME) + { + SWSS_LOG_ERROR("Key with invalid table type passed in %s, expected:%s\n", key.c_str(), APP_BUFFER_QUEUE_TABLE_NAME); + return task_process_status::task_invalid_entry; + } + vector port_names; + if (!parseNameArray(tokens[0], port_names)) + { + SWSS_LOG_ERROR("Failed to obtain port names"); + return task_process_status::task_invalid_entry; + } + if (!parseIndexRange(tokens[1], range_low, range_high)) + { + return task_process_status::task_invalid_entry; + } + ref_resolve_status resolve_result = resolveFieldRefValue(m_buffer_type_maps, buffer_profile_field_name, tuple, sai_buffer_profile); + if (ref_resolve_status::success != resolve_result) + { + SWSS_LOG_ERROR("Missing or invalid buffer profile reference specified for:%s\n", key.c_str()); + return task_process_status::task_need_retry; + } + sai_attribute_t attr; + attr.id = SAI_QUEUE_ATTR_BUFFER_PROFILE_ID; + attr.value.oid = sai_buffer_profile; + for (string port_name : port_names) + { + Port port; + SWSS_LOG_DEBUG("processing port:%s", port_name.c_str()); + if (!m_portsOrch->getPort(port_name, port)) + { + SWSS_LOG_ERROR("Port with alias:%s not found\n", port_name.c_str()); + return task_process_status::task_invalid_entry; + } + for (size_t ind = range_low; ind <= range_high; ind++) + { + sai_object_id_t queue_id; + SWSS_LOG_DEBUG("processing queue:%d", ind); + if (!port.getQueue(ind, queue_id)) + { + SWSS_LOG_ERROR("Invalid queue index specified:%d", ind); + return task_process_status::task_invalid_entry; + } + SWSS_LOG_DEBUG("Applying buffer profile:0x%llx to queue index:%d, queue sai_id:0x%llx", sai_buffer_profile, ind, queue_id); + sai_status_t sai_status = sai_queue_api->set_queue_attribute(queue_id, &attr); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set queue's buffer profile attribute, status:%d", sai_status); + return task_process_status::task_failed; + } + } + } + return task_process_status::task_success; +} + +/* +Input sample "BUFFER_PG_TABLE:Ethernet4,Ethernet45:10-15" +*/ +task_process_status BufferOrch::processPriorityGroup(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + auto it = consumer.m_toSync.begin(); + KeyOpFieldsValuesTuple tuple = it->second; + sai_object_id_t sai_buffer_profile; + string key = kfvKey(tuple); + string op = kfvOp(tuple); + vector tokens; + sai_uint32_t range_low, range_high; + + SWSS_LOG_DEBUG("processing:%s", key.c_str()); + if (!tokenizeString(key, delimiter, tokens)) + { + return task_process_status::task_invalid_entry; + } + if (tokens.size() != 2) + { + SWSS_LOG_ERROR("malformed key:%s. Must contain 2 tokens\n", key.c_str()); + return task_process_status::task_invalid_entry; + } + if (consumer.m_consumer->getTableName() != APP_BUFFER_PG_TABLE_NAME) + { + SWSS_LOG_ERROR("Key with invalid table type passed in %s, expected:%s\n", key.c_str(), APP_BUFFER_PG_TABLE_NAME); + return task_process_status::task_invalid_entry; + } + vector port_names; + if (!parseNameArray(tokens[0], port_names)) + { + SWSS_LOG_ERROR("Failed to obtain port names"); + return task_process_status::task_invalid_entry; + } + if (!parseIndexRange(tokens[1], range_low, range_high)) + { + SWSS_LOG_ERROR("Failed to obtain pg range values"); + return task_process_status::task_invalid_entry; + } + ref_resolve_status resolve_result = resolveFieldRefValue(m_buffer_type_maps, buffer_profile_field_name, tuple, sai_buffer_profile); + if (ref_resolve_status::success != resolve_result) + { + SWSS_LOG_ERROR("Missing or invalid buffer profile reference specified for:%s\n", key.c_str()); + return task_process_status::task_need_retry; + } + sai_attribute_t attr; + attr.id = SAI_INGRESS_PRIORITY_GROUP_ATTR_BUFFER_PROFILE; + attr.value.oid = sai_buffer_profile; + for (string port_name : port_names) + { + Port port; + SWSS_LOG_DEBUG("processing port:%s", port_name.c_str()); + if (!m_portsOrch->getPort(port_name, port)) + { + SWSS_LOG_ERROR("Port with alias:%s not found\n", port_name.c_str()); + return task_process_status::task_invalid_entry; + } + for (size_t ind = range_low; ind <= range_high; ind++) + { + sai_object_id_t pg_id; + SWSS_LOG_DEBUG("processing pg:%d", ind); + if (!port.getPG(ind, pg_id)) + { + SWSS_LOG_ERROR("Invalid pg index specified:%d", ind); + return task_process_status::task_invalid_entry; + } + SWSS_LOG_DEBUG("Applying buffer profile:0x%llx to port:%s pg index:%d, pg sai_id:0x%llx", sai_buffer_profile, port_name.c_str(), ind, pg_id); + sai_status_t sai_status = sai_buffer_api->set_ingress_priority_group_attr(pg_id, &attr); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set port:%s pg:%d buffer profile attribute, status:%d", port_name.c_str(), ind, sai_status); + return task_process_status::task_failed; + } + } + } + return task_process_status::task_success; +} + +/* +Input sample:"[BUFFER_PROFILE_TABLE:i_port.profile0],[BUFFER_PROFILE_TABLE:i_port.profile1]" +*/ +task_process_status BufferOrch::processIngressBufferProfileList(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + auto it = consumer.m_toSync.begin(); + KeyOpFieldsValuesTuple tuple = it->second; + Port port; + string key = kfvKey(tuple); + string op = kfvOp(tuple); + + SWSS_LOG_DEBUG("processing:%s", key.c_str()); + if (consumer.m_consumer->getTableName() != APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME) + { + SWSS_LOG_ERROR("Key with invalid table type passed in %s, expected:%s\n", key.c_str(), APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME); + return task_process_status::task_invalid_entry; + } + vector port_names; + if (!parseNameArray(key, port_names)) + { + SWSS_LOG_ERROR("Failed to obtain port names"); + return task_process_status::task_invalid_entry; + } + vector profile_list; + if (resolveFieldRefArray(m_buffer_type_maps, buffer_profile_list_field_name, tuple, profile_list)) + { + SWSS_LOG_ERROR("Missing or invalid buffer profile reference specified for:%s\n", key.c_str()); + return task_process_status::task_need_retry; + } + sai_attribute_t attr; + attr.id = SAI_PORT_ATTR_QOS_INGRESS_BUFFER_PROFILE_LIST; + attr.value.objlist.count = profile_list.size(); + attr.value.objlist.list = profile_list.data(); + for (string port_name : port_names) + { + if (!m_portsOrch->getPort(port_name, port)) + { + SWSS_LOG_ERROR("Port with alias:%s not found\n", port_name.c_str()); + return task_process_status::task_invalid_entry; + } + sai_status_t sai_status = sai_port_api->set_port_attribute(port.m_port_id, &attr); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set ingress buffer profile list on port, status:%d, key:%s", sai_status, port_name.c_str()); + return task_process_status::task_failed; + } + } + return task_process_status::task_success; +} + +/* +Input sample:"[BUFFER_PROFILE_TABLE:e_port.profile0],[BUFFER_PROFILE_TABLE:e_port.profile1]" +*/ +task_process_status BufferOrch::processEgressBufferProfileList(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + auto it = consumer.m_toSync.begin(); + KeyOpFieldsValuesTuple tuple = it->second; + Port port; + string key = kfvKey(tuple); + string op = kfvOp(tuple); + SWSS_LOG_DEBUG("processing:%s", key.c_str()); + if (consumer.m_consumer->getTableName() != APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME) + { + SWSS_LOG_ERROR("Key with invalid table type passed in %s, expected:%s\n", key.c_str(), APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME); + return task_process_status::task_invalid_entry; + } + vector port_names; + if (!parseNameArray(key, port_names)) + { + SWSS_LOG_ERROR("Failed to obtain port names"); + return task_process_status::task_invalid_entry; + } + vector profile_list; + if (resolveFieldRefArray(m_buffer_type_maps, buffer_profile_list_field_name, tuple, profile_list)) + { + SWSS_LOG_ERROR("Missing or invalid buffer profile reference specified for:%s\n", key.c_str()); + return task_process_status::task_invalid_entry; + } + sai_attribute_t attr; + attr.id = SAI_PORT_ATTR_QOS_EGRESS_BUFFER_PROFILE_LIST; + attr.value.objlist.count = profile_list.size(); + attr.value.objlist.list = profile_list.data(); + for (string port_name : port_names) + { + if (!m_portsOrch->getPort(port_name, port)) + { + SWSS_LOG_ERROR("Port with alias:%s not found\n", port_name.c_str()); + return task_process_status::task_invalid_entry; + } + sai_status_t sai_status = sai_port_api->set_port_attribute(port.m_port_id, &attr); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set egress buffer profile list on port, status:%d, key:%s", sai_status, port_name.c_str()); + return task_process_status::task_failed; + } + } + return task_process_status::task_invalid_entry; +} + +void BufferOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + if (consumer.m_toSync.empty()) + { + return; + } + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple tuple = it->second; + string map_type_name = consumer.m_consumer->getTableName(); + if (m_buffer_type_maps.find(map_type_name) == m_buffer_type_maps.end()) + { + SWSS_LOG_ERROR("Unrecognised qos table encountered:%s\n", map_type_name.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + if (m_bufferHandlerMap.find(map_type_name) == m_bufferHandlerMap.end()) + { + SWSS_LOG_ERROR("No handler for key:%s found.\n", map_type_name.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + task_process_status task_status = (this->*(m_bufferHandlerMap[map_type_name]))(consumer); + switch(task_status) + { + case task_process_status::task_success : + it = consumer.m_toSync.erase(it); + break; + case task_process_status::task_invalid_entry: + SWSS_LOG_ERROR("Invalid task item was encountered, removing from queue"); + it = consumer.m_toSync.erase(it); + break; + case task_process_status::task_failed: + SWSS_LOG_ERROR("Processing task item failed, exiting"); + return; + case task_process_status::task_need_retry: + SWSS_LOG_ERROR("Processing task item failed, will retry"); + it++; + } + } +} + diff --git a/orchagent/bufferorch.h b/orchagent/bufferorch.h new file mode 100644 index 00000000000..785ce60567e --- /dev/null +++ b/orchagent/bufferorch.h @@ -0,0 +1,50 @@ +#ifndef SWSS_BUFFORCH_H +#define SWSS_BUFFORCH_H + +#include +#include "orch.h" +#include "portsorch.h" + +const std::string buffer_size_field_name = "size"; +const std::string buffer_pool_type_field_name = "type"; +const std::string buffer_pool_mode_field_name = "mode"; +const std::string buffer_pool_field_name = "pool"; +const std::string buffer_xon_field_name = "xon"; +const std::string buffer_xoff_field_name = "xoff"; +const std::string buffer_dynamic_th_field_name = "dynamic_th"; +const std::string buffer_static_th_field_name = "static_th"; +const std::string buffer_profile_field_name = "profile"; +const std::string buffer_value_ingress = "ingress"; +const std::string buffer_value_egress = "egress"; +const std::string buffer_pool_mode_dynamic_value = "dynamic"; +const std::string buffer_pool_mode_static_value = "static"; +const std::string range_specifier = "-"; +const std::string buffer_profile_list_field_name = "profile_list"; +const std::string comma = ","; + +class BufferOrch : public Orch +{ +public: + BufferOrch(DBConnector *db, vector &tableNames, PortsOrch *portsOrch); + static type_map m_buffer_type_maps; +private: + typedef task_process_status (BufferOrch::*buffer_table_handler)(Consumer& consumer); + typedef std::map buffer_table_handler_map; + typedef std::pair buffer_handler_pair; + + virtual void doTask(Consumer& consumer); + void initTableHandlers(); + task_process_status processBufferPool(Consumer &consumer); + task_process_status processBufferProfile(Consumer &consumer); + task_process_status processQueue(Consumer &consumer); + task_process_status processPriorityGroup(Consumer &consumer); + task_process_status processIngressBufferProfileList(Consumer &consumer); + task_process_status processEgressBufferProfileList(Consumer &consumer); + bool parseIndexRange(const string &input, sai_uint32_t &range_low, sai_uint32_t &range_high); + bool parseNameArray(const string &input, vector &port_names); +private: + PortsOrch *m_portsOrch; + buffer_table_handler_map m_bufferHandlerMap; +}; +#endif /* SWSS_BUFFORCH_H */ + diff --git a/orchagent/main.cpp b/orchagent/main.cpp index 7c5d657ee34..4c2530ac6fa 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -31,6 +31,13 @@ sai_neighbor_api_t* sai_neighbor_api; sai_next_hop_api_t* sai_next_hop_api; sai_next_hop_group_api_t* sai_next_hop_group_api; sai_route_api_t* sai_route_api; +sai_queue_api_t* sai_queue_api; +sai_scheduler_api_t* sai_scheduler_api; +sai_scheduler_group_api_t* sai_scheduler_group_api; +sai_wred_api_t* sai_wred_api; +sai_qos_map_api_t* sai_qos_map_api; +sai_buffer_api_t* sai_buffer_api; + map gProfileMap; sai_object_id_t gVirtualRouterId; @@ -83,7 +90,13 @@ void initSaiApi() sai_api_query(SAI_API_NEXT_HOP, (void **)&sai_next_hop_api); sai_api_query(SAI_API_NEXT_HOP_GROUP, (void **)&sai_next_hop_group_api); sai_api_query(SAI_API_ROUTE, (void **)&sai_route_api); - + sai_api_query(SAI_API_QUEUE, (void **)&sai_queue_api); + sai_api_query(SAI_API_SCHEDULER, (void **)&sai_scheduler_api); + sai_api_query(SAI_API_WRED, (void **)&sai_wred_api); + sai_api_query(SAI_API_QOS_MAPS, (void **)&sai_qos_map_api); + sai_api_query(SAI_API_BUFFERS, (void **)&sai_buffer_api); + sai_api_query(SAI_API_SCHEDULER_GROUP, (void **)&sai_scheduler_group_api); + sai_log_set(SAI_API_SWITCH, SAI_LOG_NOTICE); sai_log_set(SAI_API_VIRTUAL_ROUTER, SAI_LOG_NOTICE); sai_log_set(SAI_API_PORT, SAI_LOG_NOTICE); @@ -94,6 +107,12 @@ void initSaiApi() sai_log_set(SAI_API_NEXT_HOP, SAI_LOG_NOTICE); sai_log_set(SAI_API_NEXT_HOP_GROUP, SAI_LOG_NOTICE); sai_log_set(SAI_API_ROUTE, SAI_LOG_NOTICE); + sai_log_set(SAI_API_QUEUE, SAI_LOG_NOTICE); + sai_log_set(SAI_API_SCHEDULER, SAI_LOG_NOTICE); + sai_log_set(SAI_API_WRED, SAI_LOG_NOTICE); + sai_log_set(SAI_API_QOS_MAPS, SAI_LOG_NOTICE); + sai_log_set(SAI_API_BUFFERS, SAI_LOG_NOTICE); + sai_log_set(SAI_API_SCHEDULER_GROUP, SAI_LOG_NOTICE); } void initDiagShell() diff --git a/orchagent/orch.cpp b/orchagent/orch.cpp index ef717a773b7..cf19e3d6cb3 100644 --- a/orchagent/orch.cpp +++ b/orchagent/orch.cpp @@ -1,6 +1,9 @@ #include "orch.h" #include "logger.h" +#include +#include + using namespace swss; Orch::Orch(DBConnector *db, string tableName) : @@ -13,7 +16,7 @@ Orch::Orch(DBConnector *db, string tableName) : Orch::Orch(DBConnector *db, vector &tableNames) : m_db(db) { - for( auto it = tableNames.begin(); it != tableNames.end(); it++) { + for ( auto it = tableNames.begin(); it != tableNames.end(); it++) { Consumer consumer(new ConsumerTable(m_db, *it)); m_consumerMap.insert(ConsumerMapPair(*it, consumer)); } @@ -22,7 +25,7 @@ Orch::Orch(DBConnector *db, vector &tableNames) : Orch::~Orch() { delete(m_db); - for(auto it : m_consumerMap) { + for (auto it : m_consumerMap) { delete it.second.m_consumer; } } @@ -32,7 +35,7 @@ std::vector Orch::getConsumers() SWSS_LOG_ENTER(); std::vector consumers; - for(auto it : m_consumerMap) { + for (auto it : m_consumerMap) { consumers.push_back(it.second.m_consumer); } return consumers; @@ -40,8 +43,8 @@ std::vector Orch::getConsumers() bool Orch::hasConsumer(ConsumerTable *consumer) const { - for(auto it : m_consumerMap) { - if(it.second.m_consumer == consumer) { + for (auto it : m_consumerMap) { + if (it.second.m_consumer == consumer) { return true; } } @@ -51,7 +54,7 @@ bool Orch::hasConsumer(ConsumerTable *consumer) const bool Orch::execute(string tableName) { auto consumer_it = m_consumerMap.find(tableName); - if(consumer_it == m_consumerMap.end()) { + if (consumer_it == m_consumerMap.end()) { SWSS_LOG_ERROR("Unrecognized tableName:%s\n", tableName.c_str()); return false; } @@ -106,3 +109,166 @@ bool Orch::execute(string tableName) doTask(consumer); return true; } +bool Orch::tokenizeString(string str, const string &separator, vector &tokens) +{ + SWSS_LOG_ENTER(); + if (0 == separator.size()) + { + SWSS_LOG_ERROR("Invalid separator passed in:%s\n", separator.c_str()); + return false; + } + if (string::npos == str.find(separator)) + { + SWSS_LOG_ERROR("Specified separator:%s not found in input:%s\n", separator.c_str(), str.c_str()); + return false; + } + istringstream ss(str); + string tmp; + while (getline(ss, tmp, separator[0])) + { + SWSS_LOG_DEBUG("extracted token:%s", tmp.c_str()); + tokens.push_back(tmp); + } + return true; +} + +/* +- Validates reference is has proper format which is [table_name:object_name] +- validates table_name exists +- validates object with object_name exists +*/ +bool Orch::parseReference(type_map &type_maps, string &ref_in, string &type_name, string &object_name) +{ + SWSS_LOG_ENTER(); + SWSS_LOG_DEBUG("input:%s", ref_in.c_str()); + if (ref_in.size() < 3) + { + SWSS_LOG_ERROR("invalid reference received:%s\n", ref_in.c_str()); + return false; + } + if ((ref_in[0] != ref_start) && (ref_in[ref_in.size()-1] != ref_end)) + { + SWSS_LOG_ERROR("malformed reference:%s. Must be surrounded by [ ]\n", ref_in.c_str()); + return false; + } + string ref_content = ref_in.substr(1, ref_in.size() - 2); + vector tokens; + if (!tokenizeString(ref_content, delimiter, tokens)) + { + return false; + } + if (tokens.size() != 2) + { + SWSS_LOG_ERROR("malformed reference:%s. Must contain 2 tokens\n", ref_content.c_str()); + return false; + } + auto type_it = type_maps.find(tokens[0]); + if (type_it == type_maps.end()) + { + SWSS_LOG_ERROR("not recognized type:%s\n", tokens[0].c_str()); + return false; + } + auto obj_map = type_maps[tokens[0]]; + auto obj_it = obj_map->find(tokens[1]); + if (obj_it == obj_map->end()) + { + SWSS_LOG_ERROR("map:%s does not contain object with name:%s\n", tokens[0].c_str(), tokens[1].c_str()); + return false; + } + type_name = tokens[0]; + object_name = tokens[1]; + SWSS_LOG_DEBUG("parsed: type_name:%s, object_name:%s", type_name.c_str(), object_name.c_str()); + return true; +} + +ref_resolve_status Orch::resolveFieldRefValue( + type_map &type_maps, + const string &field_name, + KeyOpFieldsValuesTuple &tuple, + sai_object_id_t &sai_object) +{ + SWSS_LOG_ENTER(); + size_t count = 0; + for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++) + { + if (fvField(*i) == field_name) + { + SWSS_LOG_DEBUG("field:%s, value:%s", fvField(*i).c_str(), fvValue(*i).c_str()); + if (count > 1) + { + SWSS_LOG_ERROR("Singleton field with name:%s must have only 1 instance, actual count:%d\n", field_name.c_str(), count); + return ref_resolve_status::multiple_instances; + } + string ref_type_name, object_name; + if (!parseReference(type_maps, fvValue(*i), ref_type_name, object_name)) + { + return ref_resolve_status::failure; + } + sai_object = (*(type_maps[ref_type_name]))[object_name]; + count++; + } + } + if (0 == count) + { + SWSS_LOG_NOTICE("field with name:%s not found\n", field_name.c_str()); + return ref_resolve_status::field_not_found; + } + return ref_resolve_status::success; +} + +// example: [BUFFER_PROFILE_TABLE:e_port.profile0],[BUFFER_PROFILE_TABLE:e_port.profile1] +bool Orch::resolveFieldRefArray( + type_map &type_maps, + const string &field_name, + KeyOpFieldsValuesTuple &tuple, + vector &sai_object_arr) +{ + SWSS_LOG_ENTER(); + size_t count = 0; + sai_object_arr.clear(); + for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++) + { + if (fvField(*i) == field_name) + { + if (count > 1) + { + SWSS_LOG_ERROR("Singleton field with name:%s must have only 1 instance, actual count:%d\n", field_name.c_str(), count); + return false; + } + string ref_type_name, object_name; + string list = fvValue(*i); + vector list_items; + if (list.find(list_item_delimiter) != string::npos) + { + if (!tokenizeString(list, list_item_delimiter, list_items)) + { + SWSS_LOG_ERROR("Failed to tokenize buffer profile list:%s\n", list.c_str()); + return false; + } + } + else + { + list_items.push_back(list); + } + for (size_t ind = 0; ind < list_items.size(); ind++) + { + if (!parseReference(type_maps, list_items[ind], ref_type_name, object_name)) + { + SWSS_LOG_ERROR("Failed to parse profile reference:%s\n", list_items[ind].c_str()); + return false; + } + sai_object_id_t sai_obj = (*(type_maps[ref_type_name]))[object_name]; + SWSS_LOG_DEBUG("Resolved to sai_object:0x%llx, type:%s, name:%s", sai_obj, ref_type_name.c_str(), object_name.c_str()); + sai_object_arr.push_back(sai_obj); + } + count++; + } + } + if (0 == count) + { + SWSS_LOG_NOTICE("field with name:%s not found\n", field_name.c_str()); + return ref_resolve_status::field_not_found; + } + return ref_resolve_status::success; +} + diff --git a/orchagent/orch.h b/orchagent/orch.h index 2edfcec2f6a..1985b6447d8 100644 --- a/orchagent/orch.h +++ b/orchagent/orch.h @@ -15,6 +15,19 @@ extern "C" { using namespace std; using namespace swss; +const char ref_start = '['; +const char ref_end = ']'; +const std::string delimiter = ":"; +const std::string list_item_delimiter = ","; + +typedef std::map object_map; +typedef std::pair object_map_pair; + +typedef std::map type_map; +typedef std::pair type_map_pair; + + + typedef map SyncMap; struct Consumer { Consumer(ConsumerTable* consumer) :m_consumer(consumer) { } @@ -25,6 +38,22 @@ struct Consumer { typedef std::pair ConsumerMapPair; typedef map ConsumerMap; +typedef enum +{ + success, + field_not_found, + multiple_instances, + failure +} ref_resolve_status; + +typedef enum +{ + task_success, + task_invalid_entry, + task_failed, + task_need_retry +} task_process_status; + class Orch { public: @@ -32,12 +61,26 @@ class Orch Orch(DBConnector *db, vector &tableNames); ~Orch(); +public: std::vector getConsumers(); bool hasConsumer(ConsumerTable* s)const; bool execute(string tableName); protected: + ref_resolve_status resolveFieldRefValue( + type_map &type_maps, + const string &field_name, + KeyOpFieldsValuesTuple &tuple, + sai_object_id_t &sai_object); + bool parseReference(type_map &type_maps, string &ref, string &table_name, string &object_name); + bool tokenizeString(string str, const string &separator, vector &tokens); + bool resolveFieldRefArray( + type_map &type_maps, + const string &field_name, + KeyOpFieldsValuesTuple &tuple, + vector &sai_object_arr); + virtual void doTask(Consumer &consumer) = 0; private: DBConnector *m_db; diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index d8e4742f00f..6d4d95eacaf 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -31,6 +31,24 @@ bool OrchDaemon::init() m_intfsO = new IntfsOrch(m_applDb, APP_INTF_TABLE_NAME, m_portsO); m_neighO = new NeighOrch(m_applDb, APP_NEIGH_TABLE_NAME, m_portsO); m_routeO = new RouteOrch(m_applDb, APP_ROUTE_TABLE_NAME, m_portsO, m_neighO); + std::vector qos_tables = { + APP_TC_TO_QUEUE_MAP_TABLE_NAME, + APP_SCHEDULER_TABLE_NAME, + APP_DSCP_TO_TC_MAP_TABLE_NAME, + APP_QUEUE_TABLE_NAME, + APP_PORT_QOS_MAP_TABLE_NAME, + APP_WRED_PROFILE_TABLE_NAME + }; + m_qosO = new QosOrch(m_applDb, qos_tables, m_portsO); + std::vector buffer_tables = { + APP_BUFFER_POOL_TABLE_NAME, + APP_BUFFER_PROFILE_TABLE_NAME, + APP_BUFFER_QUEUE_TABLE_NAME, + APP_BUFFER_PG_TABLE_NAME, + APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, + APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME + }; + m_bufferO = new BufferOrch(m_applDb, buffer_tables, m_portsO); m_select = new Select(); return true; @@ -45,6 +63,8 @@ void OrchDaemon::start() m_select->addSelectables(m_intfsO->getConsumers()); m_select->addSelectables(m_neighO->getConsumers()); m_select->addSelectables(m_routeO->getConsumers()); + m_select->addSelectables(m_qosO->getConsumers()); + m_select->addSelectables(m_bufferO->getConsumers()); while (true) { @@ -78,5 +98,9 @@ Orch *OrchDaemon::getOrchByConsumer(ConsumerTable *c) return m_neighO; if (m_routeO->hasConsumer(c)) return m_routeO; + if (m_qosO->hasConsumer(c)) + return m_qosO; + if (m_bufferO->hasConsumer(c)) + return m_bufferO; return nullptr; } diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index 76560215150..f7b798e3ddc 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -10,6 +10,8 @@ #include "intfsorch.h" #include "neighorch.h" #include "routeorch.h" +#include "qosorch.h" +#include "bufferorch.h" using namespace swss; @@ -25,10 +27,12 @@ class OrchDaemon DBConnector *m_applDb; DBConnector *m_asicDb; - PortsOrch *m_portsO; - IntfsOrch *m_intfsO; - NeighOrch *m_neighO; - RouteOrch *m_routeO; + PortsOrch *m_portsO; + IntfsOrch *m_intfsO; + NeighOrch *m_neighO; + RouteOrch *m_routeO; + QosOrch *m_qosO; + BufferOrch *m_bufferO; Select *m_select; diff --git a/orchagent/port.h b/orchagent/port.h index 51c79422f07..04f457098cd 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -27,6 +27,9 @@ class Port Port(std::string alias, Type type) : m_alias(alias), m_type(type) {}; + bool getQueue(size_t queue_ind, sai_object_id_t &queue_id); + bool getPG(size_t pg_ind, sai_object_id_t &pg); + inline bool operator<(const Port &o) const { return m_alias< o.m_alias; diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 2faba72da24..291fb32c167 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -21,6 +21,76 @@ extern MacAddress gMacAddress; #define FRONT_PANEL_PORT_VLAN_BASE 1024 +namespace swss { +bool Port::getQueue(size_t queue_ind, sai_object_id_t &queue_id) +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + attr.id = SAI_PORT_ATTR_QOS_NUMBER_OF_QUEUES; + attr.value.u32 = 0; + sai_status_t status = sai_port_api->get_port_attribute(m_port_id, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get number of queues for port:%s status:%d\n", m_alias.c_str(), status); + return false; + } + + size_t no_of_queues = attr.value.u32; + if (no_of_queues <= queue_ind) { + SWSS_LOG_ERROR("queue index:%d exceeds range:%d\n", queue_ind, no_of_queues); + return false; + } + attr.id = SAI_PORT_ATTR_QOS_QUEUE_LIST; + attr.value.objlist.count = no_of_queues; + attr.value.objlist.list = new sai_object_id_t[no_of_queues]; + + /* Get queue list */ + status = sai_port_api->get_port_attribute(m_port_id, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("fail to call sai_port_api->get_port_attribute: port:%s, status:%d", m_alias.c_str(), status); + delete[] attr.value.objlist.list; + return false; + } + queue_id = attr.value.objlist.list[queue_ind]; + delete[] attr.value.objlist.list; + return true; +} +bool Port::getPG(size_t pg_ind, sai_object_id_t &pg) +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + attr.id = SAI_PORT_ATTR_NUMBER_OF_PRIORITY_GROUPS; + attr.value.u32 = 0; + sai_status_t status = sai_port_api->get_port_attribute(m_port_id, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get number of queues for port:%s, status:%d\n", m_alias.c_str(), status); + return false; + } + + size_t no_of_pgs = attr.value.u32; + if (no_of_pgs <= pg_ind) { + SWSS_LOG_ERROR("pg index:%d exceeds range:%d\n", pg_ind, no_of_pgs); + return false; + } + attr.id = SAI_PORT_ATTR_PRIORITY_GROUP_LIST; + attr.value.objlist.count = no_of_pgs; + attr.value.objlist.list = new sai_object_id_t[no_of_pgs]; + + /* Get pg list */ + status = sai_port_api->get_port_attribute(m_port_id, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("fail to call sai_port_api->get_port_attribute: port:%s, status:%d", m_alias.c_str(), status); + delete[] attr.value.objlist.list; + return false; + } + pg = attr.value.objlist.list[pg_ind]; + delete[] attr.value.objlist.list; + return true; +} +} PortsOrch::PortsOrch(DBConnector *db, string tableName) : Orch(db, tableName) { @@ -434,7 +504,7 @@ bool PortsOrch::setupHostIntfs(sai_object_id_t id, string alias, sai_object_id_t strncpy((char *)&attr.value.chardata, alias.c_str(), HOSTIF_NAME_SIZE); attrs.push_back(attr); - sai_status_t status = sai_hostif_api->create_hostif(&host_intfs_id, attrs.size(), attrs.data()); + sai_status_t status = sai_hostif_api->create_hostif (&host_intfs_id, attrs.size(), attrs.data()); if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to create host interface\n"); diff --git a/orchagent/qosorch.cpp b/orchagent/qosorch.cpp new file mode 100644 index 00000000000..8b8dabccf59 --- /dev/null +++ b/orchagent/qosorch.cpp @@ -0,0 +1,874 @@ +#include "sai.h" + +#include "qosorch.h" +#include "logger.h" + +#include +#include + +extern sai_port_api_t *sai_port_api; +extern sai_queue_api_t *sai_queue_api; +extern sai_scheduler_api_t *sai_scheduler_api; +extern sai_wred_api_t *sai_wred_api; +extern sai_qos_map_api_t *sai_qos_map_api; +extern sai_scheduler_group_api_t *sai_scheduler_group_api; +extern sai_switch_api_t *sai_switch_api; + +type_map QosOrch::m_qos_type_maps = { + {APP_DSCP_TO_TC_MAP_TABLE_NAME, new object_map()}, + {APP_TC_TO_QUEUE_MAP_TABLE_NAME, new object_map()}, + {APP_SCHEDULER_TABLE_NAME, new object_map()}, + {APP_WRED_PROFILE_TABLE_NAME, new object_map()}, + {APP_PORT_QOS_MAP_TABLE_NAME, new object_map()}, + {APP_QUEUE_TABLE_NAME, new object_map()} +}; + +task_process_status QosMapHandler::processWorkItem(Consumer& consumer) +{ + SWSS_LOG_ENTER(); + sai_object_id_t sai_object = SAI_NULL_OBJECT_ID; + auto it = consumer.m_toSync.begin(); + KeyOpFieldsValuesTuple tuple = it->second; + string qos_object_name = kfvKey(tuple); + string qos_map_type_name = consumer.m_consumer->getTableName(); + string op = kfvOp(tuple); + + if (!isValidTable(qos_map_type_name)) + { + return task_process_status::task_invalid_entry; + } + + if (QosOrch::getTypeMap()[qos_map_type_name]->find(qos_object_name) != QosOrch::getTypeMap()[qos_map_type_name]->end()) + { + sai_object = (*(QosOrch::getTypeMap()[qos_map_type_name]))[qos_object_name]; + } + if (op == SET_COMMAND) + { + std::vector attributes; + if (!convertFieldValuesToAttributes(tuple, attributes)) + { + return task_process_status::task_invalid_entry; + } + if (SAI_NULL_OBJECT_ID != sai_object) + { + if (!modifyQosMap(sai_object, attributes)) + { + SWSS_LOG_ERROR("Failed to set settings to existing dscp_to_tc map. db name:%s sai object:%llx", + qos_object_name.c_str(), sai_object); + freeAttribResources(attributes); + return task_process_status::task_failed; + } + } + else { + sai_object = addQosMap(attributes); + if (sai_object == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("Failed to create dscp_to_tc map. db name:%s", qos_object_name.c_str()); + freeAttribResources(attributes); + return task_process_status::task_failed; + } + (*(QosOrch::getTypeMap()[qos_map_type_name]))[qos_object_name] = sai_object; + } + freeAttribResources(attributes); + } + else if (op == DEL_COMMAND) + { + if (SAI_NULL_OBJECT_ID == sai_object) + { + SWSS_LOG_ERROR("Object with name:%s not found.\n", qos_object_name.c_str()); + return task_process_status::task_invalid_entry; + } + if (!removeQosMap(sai_object)) + { + SWSS_LOG_ERROR("Failed to remove dscp_to_tc map. db name:%s sai object:%llx", qos_object_name.c_str(), sai_object); + return task_process_status::task_failed; + } + auto it_to_delete = (QosOrch::getTypeMap()[qos_map_type_name])->find(qos_object_name); + (QosOrch::getTypeMap()[qos_map_type_name])->erase(it_to_delete); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + return task_process_status::task_invalid_entry; + } + return task_process_status::task_success; +} + +void DscpToTcMapHandler::freeAttribResources(std::vector &attributes) +{ + SWSS_LOG_ENTER(); + delete[] attributes[0].value.qosmap.list; +} + +bool DscpToTcMapHandler::isValidTable(string &tableName) +{ + SWSS_LOG_ENTER(); + if (tableName != APP_DSCP_TO_TC_MAP_TABLE_NAME) + { + SWSS_LOG_ERROR("invalid table type passed in %s, expected:%s\n", tableName.c_str(), APP_DSCP_TO_TC_MAP_TABLE_NAME); + return false; + } + return true; +} +bool DscpToTcMapHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, std::vector &attributes) +{ + SWSS_LOG_ENTER(); + sai_attribute_t list_attr; + sai_qos_map_list_t dscp_map_list; + dscp_map_list.count = kfvFieldsValues(tuple).size(); + dscp_map_list.list = new sai_qos_map_t[dscp_map_list.count]; + uint32_t ind = 0; + for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++, ind++) + { + dscp_map_list.list[ind].key.dscp = std::stoi(fvField(*i)); + dscp_map_list.list[ind].value.tc = std::stoi(fvValue(*i)); + SWSS_LOG_DEBUG("key.dscp:%d, value.tc:%d", dscp_map_list.list[ind].key.dscp, dscp_map_list.list[ind].value.tc); + } + list_attr.id = SAI_QOS_MAP_ATTR_MAP_TO_VALUE_LIST; + list_attr.value.qosmap.count = dscp_map_list.count; + list_attr.value.qosmap.list = dscp_map_list.list; + attributes.push_back(list_attr); + return true; +} +bool DscpToTcMapHandler::modifyQosMap(sai_object_id_t sai_object, std::vector &attributes) +{ + SWSS_LOG_ENTER(); + sai_status_t sai_status = sai_qos_map_api->set_qos_map_attribute(sai_object, &attributes[0]); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to modify dscp_to_tc map. status:%d", sai_status); + return false; + } + return true; +} +sai_object_id_t DscpToTcMapHandler::addQosMap(std::vector &attributes) +{ + SWSS_LOG_ENTER(); + sai_status_t sai_status; + sai_object_id_t sai_object; + sai_attribute_t qos_map_attrs[2]; + qos_map_attrs[0].id = SAI_QOS_MAP_ATTR_TYPE; + qos_map_attrs[0].value.u32 = SAI_QOS_MAP_DSCP_TO_TC; + qos_map_attrs[1].id = SAI_QOS_MAP_ATTR_MAP_TO_VALUE_LIST; + qos_map_attrs[1].value.qosmap.count = attributes[0].value.qosmap.count; + qos_map_attrs[1].value.qosmap.list = attributes[0].value.qosmap.list; + sai_status = sai_qos_map_api->create_qos_map(&sai_object, 2, qos_map_attrs); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to create dscp_to_tc map. status:%d", sai_status); + return SAI_NULL_OBJECT_ID; + } + SWSS_LOG_DEBUG("created QosMap object:%llx", sai_object); + return sai_object; +} +bool DscpToTcMapHandler::removeQosMap(sai_object_id_t sai_object) +{ + SWSS_LOG_ENTER(); + SWSS_LOG_DEBUG("Removing QosMap object:%llx", sai_object); + sai_status_t sai_status = sai_qos_map_api->remove_qos_map(sai_object); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to remove dscp_to_tc map. status:%d", sai_status); + return false; + } + return true; +} +task_process_status QosOrch::handleDscpToTcTable(Consumer& consumer) +{ + SWSS_LOG_ENTER(); + DscpToTcMapHandler dscp_tc_handler; + return dscp_tc_handler.processWorkItem(consumer); +} + +void TcToQueueMapHandler::freeAttribResources(std::vector &attributes) +{ + SWSS_LOG_ENTER(); + delete[] attributes[0].value.qosmap.list; +} + +bool TcToQueueMapHandler::isValidTable(string &tableName) +{ + if (tableName != APP_TC_TO_QUEUE_MAP_TABLE_NAME) + { + SWSS_LOG_ERROR("invalid table type passed in %s, expected:%s\n", tableName.c_str(), APP_TC_TO_QUEUE_MAP_TABLE_NAME); + return false; + } + return true; +} +bool TcToQueueMapHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, std::vector &attributes) +{ + SWSS_LOG_ENTER(); + sai_attribute_t list_attr; + sai_qos_map_list_t tc_map_list; + tc_map_list.count = kfvFieldsValues(tuple).size(); + tc_map_list.list = new sai_qos_map_t[tc_map_list.count]; + uint32_t ind = 0; + for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++, ind++) + { + tc_map_list.list[ind].key.tc = std::stoi(fvField(*i)); + tc_map_list.list[ind].value.queue_index = std::stoi(fvValue(*i)); + } + list_attr.id = SAI_QOS_MAP_ATTR_MAP_TO_VALUE_LIST; + list_attr.value.qosmap.count = tc_map_list.count; + list_attr.value.qosmap.list = tc_map_list.list; + attributes.push_back(list_attr); + return true; +} + +bool TcToQueueMapHandler::modifyQosMap(sai_object_id_t sai_object, std::vector &attributes) +{ + SWSS_LOG_ENTER(); + sai_status_t sai_status = sai_qos_map_api->set_qos_map_attribute(sai_object, &attributes[0]); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to moify tc_to_queue map. status:%d", sai_status); + return false; + } + return true; +} + +sai_object_id_t TcToQueueMapHandler::addQosMap(std::vector &attributes) +{ + SWSS_LOG_ENTER(); + sai_status_t sai_status; + sai_object_id_t sai_object; + sai_attribute_t qos_map_attrs[2]; + qos_map_attrs[0].id = SAI_QOS_MAP_ATTR_TYPE; + qos_map_attrs[0].value.s32 = SAI_QOS_MAP_TC_TO_QUEUE; + qos_map_attrs[1].id = SAI_QOS_MAP_ATTR_MAP_TO_VALUE_LIST; + qos_map_attrs[1].value.qosmap.count = attributes[0].value.qosmap.count; + qos_map_attrs[1].value.qosmap.list = attributes[0].value.qosmap.list; + sai_status = sai_qos_map_api->create_qos_map(&sai_object, 2, qos_map_attrs); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to create tc_to_queue map. status:%d", sai_status); + return SAI_NULL_OBJECT_ID; + } + return sai_object; +} + +bool TcToQueueMapHandler::removeQosMap(sai_object_id_t sai_object) +{ + SWSS_LOG_ENTER(); + sai_status_t sai_status = sai_qos_map_api->remove_qos_map(sai_object); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to remove tc_to_queue mapstatus%d", sai_status); + return false; + } + return true; +} + +task_process_status QosOrch::handleTcToQueueTable(Consumer& consumer) +{ + SWSS_LOG_ENTER(); + TcToQueueMapHandler tc_queue_handler; + return tc_queue_handler.processWorkItem(consumer); +} + +void WredMapHandler::freeAttribResources(std::vector &attributes) +{ + SWSS_LOG_ENTER(); +} + +bool WredMapHandler::isValidTable(string &tableName) +{ + SWSS_LOG_ENTER(); + if (tableName != APP_WRED_PROFILE_TABLE_NAME) + { + SWSS_LOG_ERROR("invalid table type passed in %s, expected:%s\n", tableName.c_str(), APP_WRED_PROFILE_TABLE_NAME); + return false; + } + return true; +} +bool WredMapHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, std::vector &attribs) +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++) + { + if (fvField(*i) == yellow_max_threshold_field_name) + { + attr.id = SAI_WRED_ATTR_YELLOW_ENABLE; + attr.value.booldata = true; + attribs.push_back(attr); + + attr.id = SAI_WRED_ATTR_YELLOW_MAX_THRESHOLD; + attr.value.s32 = std::stoi(fvValue(*i)); + attribs.push_back(attr); + + // set min threshold to the same value as MAX + attr.id = SAI_WRED_ATTR_YELLOW_MIN_THRESHOLD; + attr.value.s32 = std::stoi(fvValue(*i)); + attribs.push_back(attr); + } + else if (fvField(*i) == green_max_threshold_field_name) + { + attr.id = SAI_WRED_ATTR_GREEN_ENABLE; + attr.value.booldata = true; + attribs.push_back(attr); + + attr.id = SAI_WRED_ATTR_GREEN_MAX_THRESHOLD; + attr.value.s32 = std::stoi(fvValue(*i)); + attribs.push_back(attr); + + // set min threshold to the same value as MAX + attr.id = SAI_WRED_ATTR_GREEN_MIN_THRESHOLD; + attr.value.s32 = std::stoi(fvValue(*i)); + attribs.push_back(attr); + } + else { + SWSS_LOG_ERROR( "Unkonwn wred profile field:%s", fvField(*i).c_str()); + return false; + } + } + return true; +} + +bool WredMapHandler::modifyQosMap(sai_object_id_t sai_object, std::vector &attribs) +{ + SWSS_LOG_ENTER(); + sai_status_t sai_status; + for (auto attr : attribs) + { + sai_status = sai_wred_api->set_wred_attribute(sai_object, &attr); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR( "Failed to set wred profile attribute, id:%d, status:%d", attr.id, sai_status); + return false; + } + } + return true; +} + +sai_object_id_t WredMapHandler::addQosMap(std::vector &attribs) +{ + SWSS_LOG_ENTER(); + sai_status_t sai_status; + sai_object_id_t sai_object; + sai_status = sai_wred_api->create_wred_profile(&sai_object, attribs.size(), attribs.data()); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR( "Failed to create wred profile: %d", sai_status); + return false; + } + return sai_object; +} +bool WredMapHandler::removeQosMap(sai_object_id_t sai_object) +{ + SWSS_LOG_ENTER(); + sai_status_t sai_status; + sai_status = sai_wred_api->remove_wred_profile(sai_object); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to remove scheduler profile, status:%d", sai_status); + return false; + } + return true; +} + +task_process_status QosOrch::handleWredProfileTable(Consumer& consumer) +{ + SWSS_LOG_ENTER(); + WredMapHandler wred_handler; + return wred_handler.processWorkItem(consumer); +} + +QosOrch::QosOrch(DBConnector *db, vector &tableNames, PortsOrch *portsOrch) : + Orch(db, tableNames), m_portsOrch(portsOrch) +{ + SWSS_LOG_ENTER(); + initTableHandlers(); +}; + +type_map& QosOrch::getTypeMap() +{ + SWSS_LOG_ENTER(); + return m_qos_type_maps; +} + +void QosOrch::initTableHandlers() +{ + SWSS_LOG_ENTER(); + m_qos_handler_map.insert(qos_handler_pair(APP_DSCP_TO_TC_MAP_TABLE_NAME, &QosOrch::handleDscpToTcTable)); + m_qos_handler_map.insert(qos_handler_pair(APP_TC_TO_QUEUE_MAP_TABLE_NAME, &QosOrch::handleTcToQueueTable)); + m_qos_handler_map.insert(qos_handler_pair(APP_SCHEDULER_TABLE_NAME, &QosOrch::handleSchedulerTable)); + m_qos_handler_map.insert(qos_handler_pair(APP_QUEUE_TABLE_NAME, &QosOrch::handleQueueTable)); + m_qos_handler_map.insert(qos_handler_pair(APP_PORT_QOS_MAP_TABLE_NAME, &QosOrch::handlePortQosMapTable)); + m_qos_handler_map.insert(qos_handler_pair(APP_WRED_PROFILE_TABLE_NAME, &QosOrch::handleWredProfileTable)); +} + +task_process_status QosOrch::handleSchedulerTable(Consumer& consumer) +{ + SWSS_LOG_ENTER(); + sai_status_t sai_status; + sai_object_id_t sai_object = SAI_NULL_OBJECT_ID; + auto it = consumer.m_toSync.begin(); + KeyOpFieldsValuesTuple tuple = it->second; + string qos_map_type_name = consumer.m_consumer->getTableName(); + string qos_object_name = kfvKey(tuple); + string op = kfvOp(tuple); + + if (qos_map_type_name != APP_SCHEDULER_TABLE_NAME) + { + SWSS_LOG_ERROR("Key with invalid table type passed in %s, expected:%s\n", qos_map_type_name.c_str(), APP_SCHEDULER_TABLE_NAME); + return task_process_status::task_invalid_entry; + } + if (m_qos_type_maps[qos_map_type_name]->find(qos_object_name) != m_qos_type_maps[qos_map_type_name]->end()) + { + sai_object = (*(m_qos_type_maps[qos_map_type_name]))[qos_object_name]; + if (sai_object == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("Error sai_object must exist for key %s\n", qos_object_name.c_str()); + return task_process_status::task_invalid_entry; + } + } + if (op == SET_COMMAND) + { + std::vector sai_attr_list; + sai_attribute_t attr; + for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++) + { + if (fvField(*i) == scheduler_algo_type_field_name) + { + attr.id = SAI_SCHEDULER_ATTR_SCHEDULING_ALGORITHM; + if (fvValue(*i) == scheduler_algo_DWRR) + { + attr.value.s32 = SAI_SCHEDULING_DWRR; + } + else if (fvValue(*i) == scheduler_algo_WRR) + { + attr.value.s32 = SAI_SCHEDULING_WRR; + } + else if (fvValue(*i) == scheduler_algo_STRICT) + { + attr.value.s32 = SAI_SCHEDULING_STRICT; + } + else { + SWSS_LOG_ERROR( "Unknown scheduler type value:%s", fvField(*i).c_str()); + return task_process_status::task_invalid_entry; + } + sai_attr_list.push_back(attr); + } + else if (fvField(*i) == scheduler_weight_field_name) + { + attr.id = SAI_SCHEDULER_ATTR_SCHEDULING_WEIGHT; + attr.value.s32 = std::stoi(fvValue(*i)); + sai_attr_list.push_back(attr); + } + else if (fvField(*i) == scheduler_priority_field_name) + { + // Skip for now, implementation TBD. + // The meaning is to be able to adjus priority of the given scheduler group. + // However currently SAI model does not provide such ability. + } + else { + SWSS_LOG_ERROR( "Unknown field:%s", fvField(*i).c_str()); + return task_process_status::task_invalid_entry; + } + if (SAI_NULL_OBJECT_ID != sai_object) + { + for (auto attr : sai_attr_list) + { + sai_status = sai_scheduler_api->set_scheduler_attribute(sai_object, &attr); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR( "fail to set scheduler attribute, id:%d", attr.id); + return task_process_status::task_failed; + } + } + } + else { + sai_status = sai_scheduler_api->create_scheduler_profile(&sai_object, sai_attr_list.size(), sai_attr_list.data()); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR( "fail to call sai_scheduler_api->create_scheduler_profile: %d", sai_status); + return task_process_status::task_failed; + } + (*(m_qos_type_maps[qos_map_type_name]))[qos_object_name] = sai_object; + } + } + } + else if (op == DEL_COMMAND) + { + if (SAI_NULL_OBJECT_ID == sai_object) + { + SWSS_LOG_ERROR("Object with name:%s not found.\n", qos_object_name.c_str()); + return task_process_status::task_invalid_entry; + } + sai_status = sai_scheduler_api->remove_scheduler_profile(sai_object); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to remove scheduler profile. db name:%s sai object:%llx", qos_object_name.c_str(), sai_object); + return task_process_status::task_failed; + } + auto it_to_delete = (m_qos_type_maps[qos_map_type_name])->find(qos_object_name); + (m_qos_type_maps[qos_map_type_name])->erase(it_to_delete); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + return task_process_status::task_invalid_entry; + } + return task_process_status::task_success; +} + + +bool QosOrch::applySchedulerToQueueSchedulerGroup(Port &port, size_t queue_ind, sai_object_id_t scheduler_profile_id) +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + sai_status_t sai_status; + sai_object_id_t queue_id; + vector groups; + vector child_groups; + uint32_t groups_count = 0; + + if (!port.getQueue(queue_ind, queue_id)) + { + SWSS_LOG_ERROR("Invalid queue index specified:%d", queue_ind); + return false; + } + + /* Get max child groups count */ + attr.id = SAI_SWITCH_ATTR_QOS_MAX_NUMBER_OF_CHILDS_PER_SCHEDULER_GROUP; + sai_status = sai_switch_api->get_switch_attribute(1, &attr); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to get number of childs per scheduler group for port:%s", port.m_alias.c_str()); + return false; + } + child_groups.resize(attr.value.u32); + + /* Get max sched groups count */ + attr.id = SAI_PORT_ATTR_QOS_NUMBER_OF_SCHEDULER_GROUPS; + sai_status = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to get number of scheduler groups for port:%s", port.m_alias.c_str()); + return false; + } + + /* Get total groups list on the port */ + groups_count = attr.value.u32; + groups.resize(groups_count); + + attr.id = SAI_PORT_ATTR_QOS_SCHEDULER_GROUP_LIST; + attr.value.objlist.list = groups.data(); + attr.value.objlist.count = groups_count; + sai_status = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to get scheduler group list for port:%s", port.m_alias.c_str()); + return false; + } + + /* Lookup groups to which queue belongs */ + for (uint32_t ii = 0; ii < groups_count ; ii++) + { + uint32_t child_count = 0; + + attr.id = SAI_SCHEDULER_GROUP_ATTR_CHILD_COUNT;//Number of queues/groups childs added to scheduler group + sai_status = sai_scheduler_group_api->get_scheduler_group_attribute(groups[ii], 1, &attr); + child_count = attr.value.u32; + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to get child count for scheduler group:0x%llx of port:%s", groups[ii], port.m_alias.c_str()); + return false; + } + + attr.id = SAI_SCHEDULER_GROUP_ATTR_CHILD_LIST; + attr.value.objlist.list = child_groups.data(); + attr.value.objlist.count = child_count; + sai_status = sai_scheduler_group_api->get_scheduler_group_attribute(groups[ii], 1, &attr); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to get child list for scheduler group:0x%llx of port:%s", groups[ii], port.m_alias.c_str()); + return false; + } + + for (uint32_t jj = 0; jj < child_count; jj++) + { + if (child_groups[jj] != queue_id) + { + continue; + } + + attr.id = SAI_SCHEDULER_GROUP_ATTR_SCHEDULER_PROFILE_ID; + attr.value.oid = scheduler_profile_id; + sai_status = sai_scheduler_group_api->set_scheduler_group_attribute(groups[ii], &attr); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed applying scheduler profile:0x%llx to scheduler group:0x%llx, port:%s", scheduler_profile_id, groups[ii], port.m_alias.c_str()); + return false; + } + SWSS_LOG_DEBUG("port:%s, scheduler_profile_id:0x%llx applied to scheduler group:0x%llx", port.m_alias.c_str(), scheduler_profile_id, groups[ii]); + return true; + } + } + return false; +} + +bool QosOrch::applyWredProfileToQueue(Port &port, size_t queue_ind, sai_object_id_t sai_wred_profile) +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + sai_status_t sai_status; + sai_object_id_t queue_id; + + if (!port.getQueue(queue_ind, queue_id)) + { + SWSS_LOG_ERROR("Invalid queue index specified:%d", queue_ind); + return false; + } + attr.id = SAI_QUEUE_ATTR_WRED_PROFILE_ID; + attr.value.oid = sai_wred_profile; + sai_status = sai_queue_api->set_queue_attribute(queue_id, &attr); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set queue attribute:%d", sai_status); + return false; + } + return true; +} + +task_process_status QosOrch::handleQueueTable(Consumer& consumer) +{ + SWSS_LOG_ENTER(); + auto it = consumer.m_toSync.begin(); + KeyOpFieldsValuesTuple tuple = it->second; + Port port; + bool result = true; + string key = kfvKey(tuple); + string op = kfvOp(tuple); + size_t queue_ind = 0; + vector tokens; + ref_resolve_status resolve_result; + // sample "QUEUE_TABLE:ETHERNET4:1" + if (!tokenizeString(key, delimiter, tokens)) + { + return task_process_status::task_invalid_entry; + } + if (tokens.size() != 2) + { + SWSS_LOG_ERROR("malformed key:%s. Must contain 2 tokens\n", key.c_str()); + return task_process_status::task_invalid_entry; + } + if (consumer.m_consumer->getTableName() != APP_QUEUE_TABLE_NAME) + { + SWSS_LOG_ERROR("Key with invalid table type passed in %s, expected:%s\n", key.c_str(), APP_QUEUE_TABLE_NAME); + return task_process_status::task_invalid_entry; + } + queue_ind = std::stoi(tokens[1]); + if (!m_portsOrch->getPort(tokens[0], port)) + { + SWSS_LOG_ERROR("Port with alias:%s not found\n", tokens[0].c_str()); + return task_process_status::task_invalid_entry; + } + + sai_object_id_t sai_scheduler_profile; + resolve_result = resolveFieldRefValue(m_qos_type_maps, scheduler_field_name, tuple, sai_scheduler_profile); + if (ref_resolve_status::success == resolve_result) + { + if (op == SET_COMMAND) + { + result = applySchedulerToQueueSchedulerGroup(port, queue_ind, sai_scheduler_profile); + } + else if (op == DEL_COMMAND) + { + // NOTE: The map is un-bound from the port. But the map itself still exists. + result = applySchedulerToQueueSchedulerGroup(port, queue_ind, SAI_NULL_OBJECT_ID); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + return task_process_status::task_invalid_entry; + } + if (!result) + { + SWSS_LOG_ERROR("Failed setting field:%s to port:%s, queue:%d, line:%d\n", scheduler_field_name.c_str(), port.m_alias.c_str(), queue_ind, __LINE__); + return task_process_status::task_failed; + } + } + else if (resolve_result != ref_resolve_status::field_not_found) + { + return task_process_status::task_need_retry; + } + + sai_object_id_t sai_wred_profile; + resolve_result = resolveFieldRefValue(m_qos_type_maps, wred_profile_field_name, tuple, sai_wred_profile); + if (ref_resolve_status::success == resolve_result) + { + if (op == SET_COMMAND) + { + result = applyWredProfileToQueue(port, queue_ind, sai_wred_profile); + } + else if (op == DEL_COMMAND) + { + // NOTE: The map is un-bound from the port. But the map itself still exists. + result = applyWredProfileToQueue(port, queue_ind, SAI_NULL_OBJECT_ID); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + return task_process_status::task_invalid_entry; + } + if (!result) + { + SWSS_LOG_ERROR("Failed setting field:%s to port:%s, queue:%d, line:%d\n", wred_profile_field_name.c_str(), port.m_alias.c_str(), queue_ind, __LINE__); + return task_process_status::task_failed; + } + } + else if (resolve_result != ref_resolve_status::field_not_found) + { + return task_process_status::task_need_retry; + } + return task_process_status::task_success; +} + +bool QosOrch::applyMapToPort(Port &port, sai_attr_id_t attr_id, sai_object_id_t map) +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + sai_status_t sai_status; + attr.id = attr_id; + attr.value.oid = map; + if (SAI_STATUS_SUCCESS != (sai_status = sai_port_api->set_port_attribute(port.m_port_id, &attr))) + { + SWSS_LOG_ERROR("Failed setting sai object:%llx for port:%s, status:%s\n", map, port.m_alias.c_str(), sai_status); + return false; + } + return true; +} + +task_process_status QosOrch::handlePortQosMapTable(Consumer& consumer) +{ + SWSS_LOG_ENTER(); + auto it = consumer.m_toSync.begin(); + KeyOpFieldsValuesTuple tuple = it->second; + Port port; + string key = kfvKey(tuple); + string op = kfvOp(tuple); + bool result = true; + sai_object_id_t sai_object; + sai_port_attr_t port_attr; + //"PORT_QOS_MAP_TABLE:ETHERNET4 + if (consumer.m_consumer->getTableName() != APP_PORT_QOS_MAP_TABLE_NAME) + { + SWSS_LOG_ERROR("Key with invalid table type passed in %s, expected:%s\n", key.c_str(), APP_PORT_QOS_MAP_TABLE_NAME); + return task_process_status::task_invalid_entry; + } + if (!m_portsOrch->getPort(key, port)) + { + SWSS_LOG_ERROR("Port with alias:%s not found. key:%s\n", key.c_str(), key.c_str()); + return task_process_status::task_invalid_entry; + } + + port_attr = SAI_PORT_ATTR_QOS_DSCP_TO_TC_MAP; + ref_resolve_status resolve_result = resolveFieldRefValue(m_qos_type_maps, dscp_to_tc_field_name, tuple, sai_object); + if (ref_resolve_status::success == resolve_result) + { + if (op == SET_COMMAND) + { + result = applyMapToPort(port, port_attr, sai_object); + } + else if (op == DEL_COMMAND) + { + // NOTE: The map is un-bound from the port. But the map itself still exists. + result = applyMapToPort(port, port_attr, SAI_NULL_OBJECT_ID); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + return task_process_status::task_invalid_entry; + } + if (!result) + { + SWSS_LOG_ERROR("Failed setting field:%s to port:%s, line:%d\n", dscp_to_tc_field_name.c_str(), port.m_alias.c_str(), __LINE__); + return task_process_status::task_failed; + } + SWSS_LOG_DEBUG("Applied field:%s to port:%s, line:%d\n", dscp_to_tc_field_name.c_str(), port.m_alias.c_str(), __LINE__); + } + else if (resolve_result != ref_resolve_status::field_not_found) + { + return task_process_status::task_need_retry; + } + + port_attr = SAI_PORT_ATTR_QOS_TC_TO_QUEUE_MAP; + resolve_result = resolveFieldRefValue(m_qos_type_maps, tc_to_queue_field_name, tuple, sai_object); + if (ref_resolve_status::success == resolve_result) + { + if (op == SET_COMMAND) + { + result = applyMapToPort(port, port_attr, sai_object); + } + else if (op == DEL_COMMAND) + { + // NOTE: The map is un-bound from the port. But the map itself still exists. + result = applyMapToPort(port, port_attr, SAI_NULL_OBJECT_ID); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + return task_process_status::task_invalid_entry; + } + if (!result) + { + SWSS_LOG_ERROR("Failed setting field:%s to port:%s, line:%d\n", tc_to_queue_field_name.c_str(), port.m_alias.c_str(), __LINE__); + return task_process_status::task_failed; + } + SWSS_LOG_DEBUG("Applied field:%s to port:%s, line:%d\n", tc_to_queue_field_name.c_str(), port.m_alias.c_str(), __LINE__); + } + else if (resolve_result != ref_resolve_status::field_not_found) + { + return task_process_status::task_need_retry; + } + return task_process_status::task_success; +} + +void QosOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + if (consumer.m_toSync.empty()) + { + return; + } + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple tuple = it->second; + // + // make sure table is recognized, and we have handler for it + // + string qos_map_type_name = consumer.m_consumer->getTableName(); + if (m_qos_type_maps.find(qos_map_type_name) == m_qos_type_maps.end()) + { + SWSS_LOG_ERROR("Unrecognised qos table encountered:%s\n", qos_map_type_name.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + if (m_qos_handler_map.find(qos_map_type_name) == m_qos_handler_map.end()) + { + SWSS_LOG_ERROR("No handler for key:%s found.\n", qos_map_type_name.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + task_process_status task_status = (this->*(m_qos_handler_map[qos_map_type_name]))(consumer); + switch(task_status) + { + case task_process_status::task_success : + it = consumer.m_toSync.erase(it); + break; + case task_process_status::task_invalid_entry : + SWSS_LOG_ERROR("Invalid task item was encountered, removing from queue. key:%s", kfvKey(tuple).c_str()); + it = consumer.m_toSync.erase(it); + break; + case task_process_status::task_failed : + SWSS_LOG_ERROR("Processing task item failed, exiting. key:%s", kfvKey(tuple).c_str()); + return; + case task_process_status::task_need_retry : + SWSS_LOG_ERROR("Processing task item failed, will retry. key:%s", kfvKey(tuple).c_str()); + it++; + } + } +} diff --git a/orchagent/qosorch.h b/orchagent/qosorch.h new file mode 100644 index 00000000000..44c81b77825 --- /dev/null +++ b/orchagent/qosorch.h @@ -0,0 +1,97 @@ +#ifndef SWSS_QOSORCH_H +#define SWSS_QOSORCH_H + +#include +#include "orch.h" +#include "portsorch.h" + +const std::string tc_to_queue_field_name = "tc_to_queue_map"; +const std::string dscp_to_tc_field_name = "dscp_to_tc_map"; +const std::string scheduler_field_name = "scheduler"; +const std::string wred_profile_field_name = "wred_profile"; +const std::string yellow_max_threshold_field_name = "yellow_max_threshold"; +const std::string green_max_threshold_field_name = "green_max_threshold"; + +const std::string scheduler_algo_type_field_name = "type"; +const std::string scheduler_algo_DWRR = "DWRR"; +const std::string scheduler_algo_WRR = "WRR"; +const std::string scheduler_algo_STRICT = "STRICT"; +const std::string scheduler_weight_field_name = "weight"; +const std::string scheduler_priority_field_name = "priority"; + +class QosMapHandler +{ +public: + task_process_status processWorkItem(Consumer& consumer); + virtual bool isValidTable(string &tableName) = 0; + virtual bool convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, std::vector &attributes) = 0; + virtual void freeAttribResources(std::vector &attributes) = 0; + virtual bool modifyQosMap(sai_object_id_t, std::vector &attributes) = 0; + virtual sai_object_id_t addQosMap(std::vector &attributes) = 0; + virtual bool removeQosMap(sai_object_id_t sai_object) = 0; +}; + +class DscpToTcMapHandler : public QosMapHandler +{ +public: + bool isValidTable(string &tableName); + bool convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, std::vector &attributes); + void freeAttribResources(std::vector &attributes); + bool modifyQosMap(sai_object_id_t, std::vector &attributes); + sai_object_id_t addQosMap(std::vector &attributes); + bool removeQosMap(sai_object_id_t sai_object); +}; + +class TcToQueueMapHandler : public QosMapHandler +{ +public: + bool isValidTable(string &tableName); + bool convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, std::vector &attributes); + void freeAttribResources(std::vector &attributes); + bool modifyQosMap(sai_object_id_t, std::vector &attributes); + sai_object_id_t addQosMap(std::vector &attributes); + bool removeQosMap(sai_object_id_t sai_object); +}; + +class WredMapHandler : public QosMapHandler +{ +public: + bool isValidTable(string &tableName); + bool convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, std::vector &attributes); + void freeAttribResources(std::vector &attributes); + bool modifyQosMap(sai_object_id_t, std::vector &attributes); + sai_object_id_t addQosMap(std::vector &attributes); + bool removeQosMap(sai_object_id_t sai_object); +}; + +class QosOrch : public Orch +{ +public: + + QosOrch(DBConnector *db, vector &tableNames, PortsOrch *portsOrch); + + static type_map& getTypeMap(); + static type_map m_qos_type_maps; +private: + virtual void doTask(Consumer& consumer); + + typedef task_process_status (QosOrch::*qos_table_handler)(Consumer& consumer); + typedef std::map qos_table_handler_map; + typedef std::pair qos_handler_pair; + + void initTableHandlers(); + task_process_status handleDscpToTcTable(Consumer& consumer); + task_process_status handleTcToQueueTable(Consumer& consumer); + task_process_status handleSchedulerTable(Consumer& consumer); + task_process_status handleQueueTable(Consumer& consumer); + bool applyWredProfileToQueue(Port &port, size_t queue_ind, sai_object_id_t sai_wred_profile); + bool applySchedulerToQueueSchedulerGroup(Port &port, size_t queue_ind, sai_object_id_t scheduler_profile_id); + task_process_status handlePortQosMapTable(Consumer& consumer); + bool applyMapToPort(Port &port, sai_attr_id_t attr_id, sai_object_id_t sai_dscp_to_tc_map); + task_process_status handleWredProfileTable(Consumer& consumer); + +private: + PortsOrch *m_portsOrch; + qos_table_handler_map m_qos_handler_map; +}; +#endif /* SWSS_QOSORCH_H */