From b829f252f95fe52f6c29c82af5065444c1ce8195 Mon Sep 17 00:00:00 2001 From: Junhua Zhai Date: Thu, 5 Dec 2024 07:44:34 +0000 Subject: [PATCH] Refact the templates of sai api implementation --- dash-pipeline/SAI/sai_api_gen.py | 3 +- .../templates/impls/p4_table_action.cpp.j2 | 73 ++++++++ .../impls/p4_table_entry_match.cpp.j2 | 24 +++ .../impls/p4_table_object_match.cpp.j2 | 66 +++++++ .../SAI/templates/impls/p4_table_util.cpp.j2 | 70 ++++++++ .../templates/impls/sai_api_func_bulk.cpp.j2 | 110 ++++++++++++ .../templates/impls/sai_api_func_quad.cpp.j2 | 161 ++++++++++++++++++ .../templates/impls/sai_api_func_stats.cpp.j2 | 33 ++++ .../SAI/templates/impls/sai_api_group.cpp.j2 | 46 +++++ dash-pipeline/SAI/utils/sai_gen/__init__.py | 3 +- .../SAI/utils/sai_gen/sai_generator.py | 3 - .../SAI/utils/sai_gen/sai_impl_generator.py | 22 +++ 12 files changed, 609 insertions(+), 5 deletions(-) create mode 100644 dash-pipeline/SAI/templates/impls/p4_table_action.cpp.j2 create mode 100644 dash-pipeline/SAI/templates/impls/p4_table_entry_match.cpp.j2 create mode 100644 dash-pipeline/SAI/templates/impls/p4_table_object_match.cpp.j2 create mode 100644 dash-pipeline/SAI/templates/impls/p4_table_util.cpp.j2 create mode 100644 dash-pipeline/SAI/templates/impls/sai_api_func_bulk.cpp.j2 create mode 100644 dash-pipeline/SAI/templates/impls/sai_api_func_quad.cpp.j2 create mode 100644 dash-pipeline/SAI/templates/impls/sai_api_func_stats.cpp.j2 create mode 100644 dash-pipeline/SAI/templates/impls/sai_api_group.cpp.j2 create mode 100644 dash-pipeline/SAI/utils/sai_gen/sai_impl_generator.py diff --git a/dash-pipeline/SAI/sai_api_gen.py b/dash-pipeline/SAI/sai_api_gen.py index b91d05f7f..ac930af78 100755 --- a/dash-pipeline/SAI/sai_api_gen.py +++ b/dash-pipeline/SAI/sai_api_gen.py @@ -16,7 +16,7 @@ from utils.dash_p4 import DashP4SAIExtensions from utils.p4ir import P4IRTree, P4VarRefGraph from utils.sai_spec import SaiSpec - from utils.sai_gen import SAIGenerator, SaiHeaderGenerator + from utils.sai_gen import SAIGenerator, SaiHeaderGenerator, SaiImplGenerator except ImportError as ie: print("Import failed for " + ie.name) exit(1) @@ -75,3 +75,4 @@ # Generate and update all SAI files SAIGenerator(dash_sai_exts).generate() SaiHeaderGenerator(sai_spec).generate() + SaiImplGenerator(sai_spec).generate() diff --git a/dash-pipeline/SAI/templates/impls/p4_table_action.cpp.j2 b/dash-pipeline/SAI/templates/impls/p4_table_action.cpp.j2 new file mode 100644 index 000000000..d0cbfec47 --- /dev/null +++ b/dash-pipeline/SAI/templates/impls/p4_table_action.cpp.j2 @@ -0,0 +1,73 @@ + {% if table.actions|length == 1 %} + {% for name, action in table.actions.items() %} + actionId = {{action.id}}; // {{name}} + //expectedParams = {{ action.attr_params|length }}; + {% endfor %} + {% else %} + // Search the action + for (uint32_t i = 0; i < attr_count; i++) + { + if (SAI_{{ api.name | upper }}_ATTR_ACTION == attr_list[i].id) + { + switch(attr_list[i].value.s32) + { + {% for name, action in table.actions.items() %} + case {{ name }}: + { + actionId = {{action.id}}; + //expectedParams = {{ action.attr_params|length }}; + break; + } + {% endfor %} + default: + DASH_LOG_ERROR("attribute value [%d] %d not supported yet", i, attr_list[i].value.s32); + break; + } + // only one action + break; + } + } + {% endif %} + action->set_action_id(actionId); + + for (uint32_t i = 0; i < attr_count; i++) + { + auto *md = sai_metadata_get_attr_metadata((sai_object_type_t)SAI_OBJECT_TYPE_{{ api.name | upper }}, attr_list[i].id); + + const char* attrName = md ? md->attridname : "unknown"; + + switch(attr_list[i].id) + { + {% set attr_id_list = [] %} + {% for name, action in table.actions.items() %} + {% for param_name, param in action.attr_params.items() %} + {% if param_name in attr_id_list %}{% continue %}{% endif %} + {% do attr_id_list.append( param_name ) %} + {% if param.skipattr == 'true' %} + {% else %} + case {{ param_name }}: + { + auto param = action->add_params(); + param->set_param_id({{param.id}}); + {{param.field}}SetVal(attr_list[i].value, param, {{param.bitwidth}}); + //matchedParams++; + {% if param.ip_is_v6_field_id != 0 %} + { + // set ip_is_v6_field_id field + auto param2 = action->add_params(); + param2->set_param_id({{param.ip_is_v6_field_id}}); + booldataSetVal((attr_list[i].value.ipaddr.addr_family == SAI_IP_ADDR_FAMILY_IPV4) ? 0 : 1, param2, 1); + //matchedParams++; + } + {% endif %} + break; + } + {% endif %} + {% endfor %} + {% endfor %} + default: + DASH_LOG_ERROR("attribute [%d] %d %s not supported yet", i, attr_list[i].id, attrName); + break; + } + } + diff --git a/dash-pipeline/SAI/templates/impls/p4_table_entry_match.cpp.j2 b/dash-pipeline/SAI/templates/impls/p4_table_entry_match.cpp.j2 new file mode 100644 index 000000000..ba5ff7c08 --- /dev/null +++ b/dash-pipeline/SAI/templates/impls/p4_table_entry_match.cpp.j2 @@ -0,0 +1,24 @@ + {% import 'templates/impls/p4_table_util.cpp.j2' as util %} + {% for key in table['keys'] %} + {% set value = 'tableEntry->' ~ key.name|lower %} + { + auto mf = matchActionEntry->add_match(); + mf->set_field_id({{key.id}}); + {% if key.match_type == 'exact' %}{{ util.set_key_exact(key, value) }} + {% elif key.match_type == 'lpm' %}{{ util.set_key_lpm(key, value) }} + {% elif key.match_type == 'ternary' %}{{ util.set_key_ternary(key, value) }} + {% elif key.match_type == 'optional' %}{{ util.set_key_optional(key, value) }} + {% elif key.match_type == 'list' %}{{ util.set_key_list(key, value) }} + {% elif key.match_type == 'range_list' %}{{ util.set_key_range_list(key, value) }} + {% endif %} + } + {% if key.ip_is_v6_field_id != 0 %} + { + // set ip_is_v6_field_id field + auto mf = matchActionEntry->add_match(); + mf->set_field_id({{key.ip_is_v6_field_id}}); + auto mf_exact = mf->mutable_exact(); + booldataSetVal(({{value}}.addr_family == SAI_IP_ADDR_FAMILY_IPV4) ? 0 : 1, mf_exact, 1); + } + {% endif %} + {% endfor %} diff --git a/dash-pipeline/SAI/templates/impls/p4_table_object_match.cpp.j2 b/dash-pipeline/SAI/templates/impls/p4_table_object_match.cpp.j2 new file mode 100644 index 000000000..d6419e5f3 --- /dev/null +++ b/dash-pipeline/SAI/templates/impls/p4_table_object_match.cpp.j2 @@ -0,0 +1,66 @@ + {% import 'templates/impls/p4_table_util.cpp.j2' as util %} + {% for key in table['keys'] %} + {% if key.is_object_key %} + auto key_mf = matchActionEntry->add_match(); + key_mf->set_field_id({{key.id}}); + auto key_mf_exact = key_mf->mutable_exact(); + // {{key.field}}SetVal(objId, key_mf_exact, {{key.bitwidth}}); + {{key.field}}SetVal(static_cast(objId), key_mf_exact, {{ key.bitwidth }}); + {% endif %} + {% endfor %} + + // SAI object table with multiple P4 table keys + // Copy P4 table keys from appropriate SAI attributes + for (uint32_t i = 0; i < attr_count; i++) + { + auto *md = sai_metadata_get_attr_metadata((sai_object_type_t)SAI_OBJECT_TYPE_{{ api.name | upper }}, attr_list[i].id); + + const char* attrName = md ? md->attridname : "unknown"; + + switch(attr_list[i].id) + { + {% for key in table['keys'] %} + {% if not key.is_object_key %} + {% set value = 'attr_list[i].value' %} + case SAI_{{ api.name | upper }}_ATTR_{{ key.name | upper }}: + { + auto mf = matchActionEntry->add_match(); + mf->set_field_id({{key.id}}); + {% filter indent(8, True) %} + {% if key.match_type == 'exact' %}{{ util.set_key_in_attr_exact(key, value) }} + {% elif key.match_type == 'lpm' %}{{ util.set_key_lpm(key, value) }} + {% elif key.match_type == 'ternary' %}{{ util.set_key_in_attr_ternary(api, key, value) }} + {% elif key.match_type == 'optional' %}{{ util.set_key_optional(key, value) }} + {% elif key.match_type == 'list' %}{{ util.set_key_list(key, value) }} + {% elif key.match_type == 'range_list' %}{{ util.set_key_range_list(key, value) }} + {% endif %} + {% endfilter %} + {% if key.ip_is_v6_field_id != 0 %} + { + // set ip_is_v6_field_id field + auto mf = matchActionEntry->add_match(); + mf->set_field_id({{key.ip_is_v6_field_id}}); + auto mf_exact = mf->mutable_exact(); + booldataSetVal(({{value}}.ipaddr.addr_family == SAI_IP_ADDR_FAMILY_IPV4) ? 0 : 1, mf_exact, 1); + } + {% endif %} + break; + } + {% endif%} + {% endfor %} + {% if table['keys'] | selectattr('match_type', 'ne', 'exact') | list | length > 0 %} + {% if table['keys'] | selectattr('match_type', 'eq', 'lpm') | list | length == 0 %} + // Table has non lpm ternary keys - add priority field + case SAI_{{ api.name | upper }}_ATTR_PRIORITY: + { + matchActionEntry->set_priority(attr_list[i].value.u32); + break; + } + {% endif %} + {% endif %} + default: + DASH_LOG_ERROR("attribute [%d] %d %s not supported yet", i, attr_list[i].id, attrName); + break; + } + } + diff --git a/dash-pipeline/SAI/templates/impls/p4_table_util.cpp.j2 b/dash-pipeline/SAI/templates/impls/p4_table_util.cpp.j2 new file mode 100644 index 000000000..a610d7f13 --- /dev/null +++ b/dash-pipeline/SAI/templates/impls/p4_table_util.cpp.j2 @@ -0,0 +1,70 @@ +{% macro set_key_exact(key, value) %} + auto mf_exact = mf->mutable_exact(); + {% if key.field in ['ipaddr','mac'] or key.bitwidth in [24] %} + {{key.field}}SetVal({{value}}, mf_exact, {{key.bitwidth}}); + {%- else %} + {{key.field}}SetVal(static_cast({{value}}), mf_exact, {{key.bitwidth}}); + {%- endif %} +{%- endmacro -%} + +{% macro set_key_in_attr_exact(key, value) %} + auto mf_exact = mf->mutable_exact(); + {{key.field}}SetVal({{value}}, mf_exact, {{key.bitwidth}}); +{%- endmacro -%} + +{% macro set_key_lpm(key, value) %} + {% if key.field == 'ipPrefix' %} + if (getPrefixLength({{value}}) == 0) + { + // https://github.com/p4lang/PI/blob/24e0a3c08c964e36d235973556b90e0ae922b894/proto/frontend/src/device_mgr.cpp#L2242-L2246 + DASH_LOG_WARN("Invalid reprsentation of 'don't care' LPM match, omit match field instead of using a prefix length of 0"); + return SAI_STATUS_SUCCESS; + } + {% endif %} + auto mf_lpm = mf->mutable_lpm(); + {{key.field}}SetVal({{value}}, mf_lpm, {{key.bitwidth}}); +{%- endmacro -%} + +{% macro set_key_in_attr_ternary(api, key, value) %} + auto mf_ternary = mf->mutable_ternary(); + {{key.field}}SetVal({{value}}, mf_ternary, {{key.bitwidth}}); + auto mask = getMaskAttr(SAI_{{ api.name | upper }}_ATTR_{{ key.name | upper }}_MASK, attr_count, attr_list); + assert(mask && "SAI_{{ api.name | upper }}_ATTR_{{ key.name | upper }}_MASK isn't provided"); + {{key.field}}SetMask(mask->value, mf_ternary, {{key.bitwidth}}); +{%- endmacro -%} + +{% macro set_key_ternary(key, value) %} + auto mf_ternary = mf->mutable_ternary(); + {{key.field}}SetVal({{value}}, mf_ternary, {{key.bitwidth}}); + {{key.field}}SetMask({{value}}_mask, mf_ternary, {{key.bitwidth}}); + matchActionEntry->set_priority(tableEntry->priority); +{%- endmacro -%} + +{% macro set_key_optional(key, value) %} + auto mf_optional = mf->mutable_optional(); + {{key.field}}SetVal({{value}}, mf_optional, {{key.bitwidth}}); +{%- endmacro -%} + +{% macro set_key_list(key, value) %} + // BMv2 doesn't support "list" match type, and we are using "optional" match in v1model as our implementation. + // Hence, here we only take the first item from the list and program it as optional match. + assert({{value}}.{{key.field}}.count == 1 && "BMv2 only supports one item in list match type"); + auto mf_optional = mf->mutable_optional(); + sai_attribute_value_t attr_val; + {% if key.field == 'ipprefixlist' %} + attr_val.ipaddr.addr_family = {{value}}.ipprefixlist.list[0].addr_family; + attr_val.ipaddr.addr = {{value}}.ipprefixlist.list[0].addr; + ipaddrSetVal(attr_val, mf_optional, {{key.bitwidth}}); + {%- else %} + attr_val.{{ key.field | replace('list', '') }} = {{value}}.{{key.field}}.list[0]; + {{ key.field | replace('list', '') }}SetVal(attr_val, mf_optional, {{key.bitwidth}}); + {%- endif %} +{%- endmacro -%} + +{% macro set_key_range_list(key, value) %} + // BMv2 doesn't support "range_list" match type, and we are using "optional" match in v1model as our implementation. + // Hence, here we only take the first item from the list and program the range start as optional match. + assert(attr_list[i].value.{{key.field}}.count == 1 && "BMv2 only supports one item in list match type"); + auto mf_optional = mf->mutable_optional(); + {{ key.field | replace('rangelist', '') }}SetVal(attr_list[i].value.{{key.field}}.list[0].min, mf_optional, {{key.bitwidth}}); +{%- endmacro -%} diff --git a/dash-pipeline/SAI/templates/impls/sai_api_func_bulk.cpp.j2 b/dash-pipeline/SAI/templates/impls/sai_api_func_bulk.cpp.j2 new file mode 100644 index 000000000..3f42a54af --- /dev/null +++ b/dash-pipeline/SAI/templates/impls/sai_api_func_bulk.cpp.j2 @@ -0,0 +1,110 @@ +{% if api.is_object == True %} +static sai_status_t dash_sai_create_{{ api.name }}s( + _In_ sai_object_id_t switch_id, + _In_ uint32_t object_count, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_object_id_t *object_id, + _Out_ sai_status_t *object_statuses) +{ + DASH_LOG_ENTER(); + return dash::DashSai::bulk_create_objects(dash_sai_create_{{ api.name }}, switch_id, object_count, attr_count, attr_list, mode, object_id, object_statuses); +} + +static sai_status_t dash_sai_remove_{{ api.name }}s( + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + DASH_LOG_ENTER(); + return dash::DashSai::bulk_remove_objects(dash_sai_remove_{{ api.name }}, object_count, object_id, mode, object_statuses); +} +{% else %} +static sai_status_t dash_sai_create_{{ api.name | replace("entry", "entries") }}( + _In_ uint32_t object_count, + _In_ const sai_{{ api.name }}_t *{{ api.name }}, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + DASH_LOG_ENTER(); + + sai_status_t agg_status = SAI_STATUS_SUCCESS; + + for (uint32_t i = 0; i < object_count; i++) + { + object_statuses[i] = dash_sai_create_{{ api.name }}(&{{ api.name }}[i], attr_count[i], attr_list[i]); + + if (object_statuses[i] != SAI_STATUS_SUCCESS) + { + agg_status = SAI_STATUS_FAILURE; + } + + if (agg_status == SAI_STATUS_FAILURE && mode == SAI_BULK_OP_ERROR_MODE_STOP_ON_ERROR) + { + return agg_status; + } + } + + return agg_status; +} + +static sai_status_t dash_sai_remove_{{ api.name | replace("entry", "entries") }}( + _In_ uint32_t object_count, + _In_ const sai_{{ api.name }}_t *{{ api.name }}, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + DASH_LOG_ENTER(); + + sai_status_t agg_status = SAI_STATUS_SUCCESS; + + for (uint32_t i = 0; i < object_count; i++) + { + object_statuses[i] = dash_sai_remove_{{ api.name }}(&{{ api.name }}[i]); + + if (object_statuses[i] != SAI_STATUS_SUCCESS) + { + agg_status = SAI_STATUS_FAILURE; + } + + if (agg_status == SAI_STATUS_FAILURE && mode == SAI_BULK_OP_ERROR_MODE_STOP_ON_ERROR) + { + return agg_status; + } + } + + return agg_status; +} +{% if api.name == 'route_entry' %} + +static sai_status_t dash_sai_set_{{ api.name | replace("entry", "entries") }}_attribute( + _In_ uint32_t object_count, + _In_ const sai_route_entry_t *route_entry, + _In_ const sai_attribute_t *attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + DASH_LOG_ENTER(); + assert(0 && "sai_set_{{ api.name | replace("entry", "entries") }}_attribute NYI"); + return SAI_STATUS_FAILURE; +} + +static sai_status_t dash_sai_get_{{ api.name | replace("entry", "entries") }}_attribute( + _In_ uint32_t object_count, + _In_ const sai_route_entry_t *route_entry, + _In_ const uint32_t *attr_count, + _Inout_ sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + DASH_LOG_ENTER(); + assert(0 && "sai_get_{{ api.name | replace("entry", "entries") }}_attribute NYI"); + return SAI_STATUS_FAILURE; +} +{% endif %} +{% endif %} + diff --git a/dash-pipeline/SAI/templates/impls/sai_api_func_quad.cpp.j2 b/dash-pipeline/SAI/templates/impls/sai_api_func_quad.cpp.j2 new file mode 100644 index 000000000..249d0209b --- /dev/null +++ b/dash-pipeline/SAI/templates/impls/sai_api_func_quad.cpp.j2 @@ -0,0 +1,161 @@ +static sai_status_t dash_sai_create_{{ api.name }}( +{% if api.is_object %} + _Out_ sai_object_id_t *{{ api.name }}_id, + _In_ sai_object_id_t switch_id, +{% else %} + _In_ const sai_{{ api.name }}_t *{{ api.name }}, +{% endif %} + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + DASH_LOG_ENTER(); + + auto attrs = dash::DashSai::populateDefaultAttributes((sai_object_type_t)SAI_OBJECT_TYPE_{{ api.name | upper }}, attr_count, attr_list); + attr_count = (uint32_t)attrs.size(); + attr_list = attrs.data(); + +{% if api.is_object %} + std::shared_ptr matchActionEntry; + pi_p4_id_t tableId = 0; + // There shall be one and only one action_type + p4::v1::TableAction* entry = nullptr; + p4::v1::Action* action = nullptr; + //auto expectedParams = 0; + //auto matchedParams = 0; + sai_object_id_t objId = 0; + // Search the action + pi_p4_id_t actionId = 0; + + {% for table in api.p4_meta.tables %} + {% if table.stage != None %} + // For stage {{ table.stage }} + {% endif %} + matchActionEntry = std::make_shared(); + tableId = {{table.id}}; + entry = matchActionEntry->mutable_action(); + action = entry->mutable_action(); + //expectedParams = 0; + //matchedParams = 0; + objId = dashSai->getNextObjectId((sai_object_type_t)SAI_OBJECT_TYPE_{{ api.name | upper }}); + + if (objId == SAI_NULL_OBJECT_ID) + { + DASH_LOG_ERROR("getNextObjectId failed for SAI_OBJECT_TYPE_{{ api.name | upper }}"); + // TODO clean resources + return SAI_STATUS_FAILURE; + } + + matchActionEntry->set_table_id(tableId); + + {% include 'templates/impls/p4_table_object_match.cpp.j2' %} + + // If there is only one action, simply set it. + // Else, search in the attrs. + {% include 'templates/impls/p4_table_action.cpp.j2' %} + + //assert((matchedParams == expectedParams)); + + //if (matchedParams != expectedParams) { + // goto ErrRet; + //} + if (false == dashSai->insertInTable(matchActionEntry, objId)) { + goto ErrRet; + } + + {% endfor %} + + *{{ api.name }}_id = objId; + return SAI_STATUS_SUCCESS; +ErrRet: + dashSai->removeFromTable(*{{ api.name }}_id); + return SAI_STATUS_FAILURE; +{% else %} + std::shared_ptr matchActionEntry = std::make_shared(); + pi_p4_id_t tableId = {{table.id}}; + matchActionEntry->set_table_id(tableId); + auto tableEntry = {{ api.name }}; + // There shall be one and only one action_type + auto entry = matchActionEntry->mutable_action(); + auto action = entry->mutable_action(); + //auto expectedParams = 0; + //auto matchedParams = 0; + pi_p4_id_t actionId; + grpc::StatusCode retCode; + + {% include 'templates/impls/p4_table_entry_match.cpp.j2' %} + + + {% include 'templates/impls/p4_table_action.cpp.j2' %} + + //assert((matchedParams == expectedParams)); + + //if (matchedParams != expectedParams) { + // goto ErrRet; + //} + // TODO: ternaly needs to set priority + retCode = dashSai->mutateTableEntry(matchActionEntry, p4::v1::Update_Type_INSERT); + + if (grpc::StatusCode::OK == retCode) + { + return SAI_STATUS_SUCCESS; + } +ErrRet: + return SAI_STATUS_FAILURE; +{% endif %} +} + +static sai_status_t dash_sai_remove_{{ api.name }}( + {% include 'templates/headers/sai_api_param_object_id.j2' %}) +{ +{% if api.is_object %} + DASH_LOG_ENTER(); + + if (dashSai->removeFromTable({{ api.name }}_id)) + { + return SAI_STATUS_SUCCESS; + } + + return SAI_STATUS_FAILURE; +{% else %} + DASH_LOG_ENTER(); + + std::shared_ptr matchActionEntry = std::make_shared(); + pi_p4_id_t tableId = {{table.id}}; + matchActionEntry->set_table_id(tableId); + auto tableEntry = {{ api.name }}; + grpc::StatusCode retCode; + + {% include 'templates/impls/p4_table_entry_match.cpp.j2' %} + + retCode = dashSai->mutateTableEntry(matchActionEntry, p4::v1::Update_Type_DELETE); + + if (grpc::StatusCode::OK == retCode) + { + return SAI_STATUS_SUCCESS; + } + +ErrRet: + + return SAI_STATUS_FAILURE; +{% endif %} +} + +static sai_status_t dash_sai_set_{{ api.name }}_attribute( + {% include 'templates/headers/sai_api_param_object_id.j2' %}, + _In_ const sai_attribute_t *attr) +{ + DASH_LOG_ENTER(); + assert(0 && "sai_set_{{ api.name }}_attribute NYI"); + return SAI_STATUS_FAILURE; +} + +static sai_status_t dash_sai_get_{{ api.name }}_attribute( + {% include 'templates/headers/sai_api_param_object_id.j2' %}, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) +{ + DASH_LOG_ENTER(); + assert(0 && "sai_get_{{ api.name }}_attribute NYI"); + return SAI_STATUS_FAILURE; +} + diff --git a/dash-pipeline/SAI/templates/impls/sai_api_func_stats.cpp.j2 b/dash-pipeline/SAI/templates/impls/sai_api_func_stats.cpp.j2 new file mode 100644 index 000000000..8645d3e1c --- /dev/null +++ b/dash-pipeline/SAI/templates/impls/sai_api_func_stats.cpp.j2 @@ -0,0 +1,33 @@ +static sai_status_t dash_sai_get_{{ api.name }}_stats( + {% include 'templates/headers/sai_api_param_object_id.j2' %}, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _Out_ uint64_t *counters) +{ + DASH_LOG_ENTER(); + assert(0 && "sai_get_{{ api.name }}_stats NYI"); + return SAI_STATUS_FAILURE; +} + +static sai_status_t dash_sai_get_{{ api.name }}_stats_ext( + {% include 'templates/headers/sai_api_param_object_id.j2' %}, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _In_ sai_stats_mode_t mode, + _Out_ uint64_t *counters) +{ + DASH_LOG_ENTER(); + assert(0 && "sai_get_{{ api.name }}_stats_ext NYI"); + return SAI_STATUS_FAILURE; +} + +static sai_status_t dash_sai_clear_{{ api.name }}_stats( + {% include 'templates/headers/sai_api_param_object_id.j2' %}, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids) +{ + DASH_LOG_ENTER(); + assert(0 && "sai_clear_{{ api.name }}_stats NYI"); + return SAI_STATUS_FAILURE; +} + diff --git a/dash-pipeline/SAI/templates/impls/sai_api_group.cpp.j2 b/dash-pipeline/SAI/templates/impls/sai_api_group.cpp.j2 new file mode 100644 index 000000000..42ab5ba24 --- /dev/null +++ b/dash-pipeline/SAI/templates/impls/sai_api_group.cpp.j2 @@ -0,0 +1,46 @@ +#include "utils.h" +#include "saiimpl.h" + +extern "C" { +#include "saimetadata.h" +} + +using namespace dash::utils; + + +{% for api in api_group.sai_apis %} +{% set table = api.p4_meta.tables[0] -%} +{% include 'templates/impls/sai_api_func_quad.cpp.j2' %} + +{% if api.stats | length > 0 %} +{% include 'templates/impls/sai_api_func_stats.cpp.j2' %} + +{% endif %} +{% include 'templates/impls/sai_api_func_bulk.cpp.j2' %} + +{% endfor %} +/* TODO [cs] Generate .h file for _impl to use within sai_api_query() */ +sai_{{ api_group.name }}_api_t dash_sai_{{ api_group.name }}_api_impl = { +{% for api in api_group.sai_apis %} + .create_{{ api.name }} = dash_sai_create_{{ api.name }}, + .remove_{{ api.name }} = dash_sai_remove_{{ api.name }}, + .set_{{ api.name }}_attribute = dash_sai_set_{{ api.name }}_attribute, + .get_{{ api.name }}_attribute = dash_sai_get_{{ api.name }}_attribute, +{% if api.stats | length > 0 %} + .get_{{ api.name }}_stats = dash_sai_get_{{ api.name }}_stats, + .get_{{ api.name }}_stats_ext = dash_sai_get_{{ api.name }}_stats_ext, + .clear_{{ api.name }}_stats = dash_sai_clear_{{ api.name }}_stats, +{% endif %} +{% if api.is_object %} + .create_{{ api.name }}s = dash_sai_create_{{ api.name }}s, + .remove_{{ api.name }}s = dash_sai_remove_{{ api.name }}s, +{% else %} + .create_{{ api.name | replace("entry", "entries") }} = dash_sai_create_{{ api.name | replace("entry", "entries") }}, + .remove_{{ api.name | replace("entry", "entries") }} = dash_sai_remove_{{ api.name | replace("entry", "entries") }}, +{% endif %} +{% if api.name == 'route_entry' %} + .set_{{ api.name | replace("entry", "entries") }}_attribute = dash_sai_set_{{ api.name | replace("entry", "entries") }}_attribute, + .get_{{ api.name | replace("entry", "entries") }}_attribute = dash_sai_get_{{ api.name | replace("entry", "entries") }}_attribute, +{% endif %} +{% endfor %} +}; diff --git a/dash-pipeline/SAI/utils/sai_gen/__init__.py b/dash-pipeline/SAI/utils/sai_gen/__init__.py index 71d9e9a12..00b2a56a0 100644 --- a/dash-pipeline/SAI/utils/sai_gen/__init__.py +++ b/dash-pipeline/SAI/utils/sai_gen/__init__.py @@ -1,4 +1,5 @@ from .sai_file_updater import SAIFileUpdater from .sai_template_renderer import SAITemplateRenderer from .sai_generator import SAIGenerator -from .sai_header_generator import SaiHeaderGenerator \ No newline at end of file +from .sai_header_generator import SaiHeaderGenerator +from .sai_impl_generator import SaiImplGenerator diff --git a/dash-pipeline/SAI/utils/sai_gen/sai_generator.py b/dash-pipeline/SAI/utils/sai_gen/sai_generator.py index 455cad4e7..d3b072e69 100644 --- a/dash-pipeline/SAI/utils/sai_gen/sai_generator.py +++ b/dash-pipeline/SAI/utils/sai_gen/sai_generator.py @@ -44,9 +44,6 @@ def generate_sai_api_extensions(self, sai_api: DashP4TableGroup) -> None: if sai_api.api_type != "underlay": self.generate_dash_sai_definitions_for_api(unique_sai_api) - # Generate SAI API implementation for all APIs. - self.generate_sai_impl_file_for_api(sai_api) - def generate_dash_sai_definitions_for_api(self, sai_api: DashP4TableGroup) -> None: # SAI header file sai_header_file_name = ( diff --git a/dash-pipeline/SAI/utils/sai_gen/sai_impl_generator.py b/dash-pipeline/SAI/utils/sai_gen/sai_impl_generator.py new file mode 100644 index 000000000..5cd1d1680 --- /dev/null +++ b/dash-pipeline/SAI/utils/sai_gen/sai_impl_generator.py @@ -0,0 +1,22 @@ +from utils.sai_spec import SaiSpec, SaiApiGroup +from .sai_template_renderer import SAITemplateRenderer + + +class SaiImplGenerator: + def __init__(self, sai_spec: SaiSpec): + self.sai_spec: SaiSpec = sai_spec + + def generate(self) -> None: + print("\nGenerating SAI API implementation for all APIs ...") + + for api_group in self.sai_spec.api_groups: + self._generate_sai_api_group(api_group) + + def _generate_sai_api_group(self, api_group: SaiApiGroup) -> None: + print(f"Generating SAI API implementation for API group: {api_group.name} ...") + + # SAI implementation file + sai_impl_file_name = f"sai{api_group.name.replace('_', '')}.cpp" + SAITemplateRenderer("templates/impls/sai_api_group.cpp.j2").render_to_file( + f"lib/{sai_impl_file_name}", api_group = api_group + )