Skip to content

Commit

Permalink
[sai-gen] Refactor the templates of libsai implementation (#648)
Browse files Browse the repository at this point in the history
Base on #647, refactor the
templates of libsai implementation
to make it more readable and maintainable. It replaces one single
template `saiapi.cpp.j2` with several
small templates under directory `SAI/templates/impls`.

The changes will not change any generated cpp code for sai api
implementation:
```
junhuazhai@junhuazhai-dev-vm:~/workspace/DASH/dash-pipeline$ diff -Nur -x '*.[od]' ~/DASH/dash-pipeline/SAI/lib/ SAI/lib
junhuazhai@junhuazhai-dev-vm:~/workspace/DASH/dash-pipeline$
```
  • Loading branch information
jimmyzhai authored Dec 11, 2024
1 parent 3013f33 commit e16ceb4
Show file tree
Hide file tree
Showing 12 changed files with 609 additions and 5 deletions.
3 changes: 2 additions & 1 deletion dash-pipeline/SAI/sai_api_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -75,3 +75,4 @@
# Generate and update all SAI files
SAIGenerator(dash_sai_exts).generate()
SaiHeaderGenerator(sai_spec).generate()
SaiImplGenerator(sai_spec).generate()
73 changes: 73 additions & 0 deletions dash-pipeline/SAI/templates/impls/p4_table_action.cpp.j2
Original file line number Diff line number Diff line change
@@ -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;
}
}

24 changes: 24 additions & 0 deletions dash-pipeline/SAI/templates/impls/p4_table_entry_match.cpp.j2
Original file line number Diff line number Diff line change
@@ -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 %}
66 changes: 66 additions & 0 deletions dash-pipeline/SAI/templates/impls/p4_table_object_match.cpp.j2
Original file line number Diff line number Diff line change
@@ -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<uint{{ key.bitwidth }}_t>(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;
}
}

70 changes: 70 additions & 0 deletions dash-pipeline/SAI/templates/impls/p4_table_util.cpp.j2
Original file line number Diff line number Diff line change
@@ -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<uint{{key.bitwidth}}_t>({{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 -%}
110 changes: 110 additions & 0 deletions dash-pipeline/SAI/templates/impls/sai_api_func_bulk.cpp.j2
Original file line number Diff line number Diff line change
@@ -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 %}

Loading

0 comments on commit e16ceb4

Please sign in to comment.