From 8dd75ce598d879cb68d55b0850163f126e5c7302 Mon Sep 17 00:00:00 2001 From: Riff Date: Tue, 22 Oct 2024 14:28:03 -0700 Subject: [PATCH 1/2] [dash-p4] Add tunnel member table and tunnel next hop table. (#622) To support the ECMP group in the tunnel, this change adds the tunnel member and tunnel next hop tables using the same way as how next hop group works today in SAI. The detailed design is following the HLD defined here: https://github.com/sonic-net/DASH/blob/main/documentation/private-link-service/private-link-service.md. --- dash-pipeline/SAI/specs/dash_tunnel.yaml | 101 +++++++++++++- dash-pipeline/SAI/specs/sai_spec.yaml | 2 + dash-pipeline/SAI/templates/saiapi.cpp.j2 | 22 ++-- dash-pipeline/SAI/templates/saiapi.h.j2 | 2 + .../SAI/utils/dash_p4/dash_p4_table.py | 12 +- .../utils/dash_p4/dash_p4_table_attribute.py | 32 ++++- .../SAI/utils/dash_p4/dash_p4_table_key.py | 5 + dash-pipeline/bmv2/README.md | 4 +- dash-pipeline/bmv2/dash_metadata.p4 | 4 + dash-pipeline/bmv2/stages/tunnel_stage.p4 | 123 ++++++++++++++++-- 10 files changed, 273 insertions(+), 34 deletions(-) diff --git a/dash-pipeline/SAI/specs/dash_tunnel.yaml b/dash-pipeline/SAI/specs/dash_tunnel.yaml index b73273e6b..d90724899 100644 --- a/dash-pipeline/SAI/specs/dash_tunnel.yaml +++ b/dash-pipeline/SAI/specs/dash_tunnel.yaml @@ -30,7 +30,7 @@ sai_apis: attr_value_field: s32 default: SAI_DASH_ENCAPSULATION_VXLAN isresourcetype: false - flags: CREATE_AND_SET + flags: CREATE_ONLY object_name: null allow_null: false valid_only: null @@ -43,6 +43,32 @@ sai_apis: attr_value_field: u32 default: '0' isresourcetype: false + flags: CREATE_ONLY + object_name: null + allow_null: false + valid_only: null + is_vlan: false + deprecated: false + - !!python/object:utils.sai_spec.sai_attribute.SaiAttribute + name: SAI_DASH_TUNNEL_ATTR_MAX_MEMBER_SIZE + description: Action parameter max member size + type: sai_uint32_t + attr_value_field: u32 + default: '1' + isresourcetype: false + flags: CREATE_ONLY + object_name: null + allow_null: false + valid_only: null + is_vlan: false + deprecated: false + - !!python/object:utils.sai_spec.sai_attribute.SaiAttribute + name: SAI_DASH_TUNNEL_ATTR_SIP + description: Action parameter sip + type: sai_ip_address_t + attr_value_field: ipaddr + default: 0.0.0.0 + isresourcetype: false flags: CREATE_AND_SET object_name: null allow_null: false @@ -59,3 +85,76 @@ sai_apis: name: default id: 27891720 attr_param_id: {} +- !!python/object:utils.sai_spec.sai_api.SaiApi + name: dash_tunnel_member + description: DASH tunnel member + is_object: true + enums: [] + structs: [] + attributes: + - !!python/object:utils.sai_spec.sai_attribute.SaiAttribute + name: SAI_DASH_TUNNEL_MEMBER_ATTR_DASH_TUNNEL_ID + description: Action parameter DASH tunnel id + type: sai_object_id_t + attr_value_field: u16 + default: null + isresourcetype: false + flags: MANDATORY_ON_CREATE | CREATE_ONLY + object_name: SAI_OBJECT_TYPE_DASH_TUNNEL + allow_null: false + valid_only: null + is_vlan: false + deprecated: false + - !!python/object:utils.sai_spec.sai_attribute.SaiAttribute + name: SAI_DASH_TUNNEL_MEMBER_ATTR_DASH_TUNNEL_NEXT_HOP_ID + description: Action parameter DASH tunnel next hop id + type: sai_object_id_t + attr_value_field: u16 + default: null + isresourcetype: false + flags: MANDATORY_ON_CREATE | CREATE_AND_SET + object_name: SAI_OBJECT_TYPE_DASH_TUNNEL_NEXT_HOP + allow_null: false + valid_only: null + is_vlan: false + deprecated: false + stats: [] + p4_meta: !!python/object:utils.sai_spec.sai_api_p4_meta.SaiApiP4Meta + tables: + - !!python/object:utils.sai_spec.sai_api_p4_meta.SaiApiP4MetaTable + id: 40012474 + actions: + default: !!python/object:utils.sai_spec.sai_api_p4_meta.SaiApiP4MetaAction + name: default + id: 23738116 + attr_param_id: {} +- !!python/object:utils.sai_spec.sai_api.SaiApi + name: dash_tunnel_next_hop + description: DASH tunnel next hop + is_object: true + enums: [] + structs: [] + attributes: + - !!python/object:utils.sai_spec.sai_attribute.SaiAttribute + name: SAI_DASH_TUNNEL_NEXT_HOP_ATTR_DIP + description: Action parameter dip + type: sai_ip_address_t + attr_value_field: ipaddr + default: 0.0.0.0 + isresourcetype: false + flags: CREATE_AND_SET + object_name: null + allow_null: false + valid_only: null + is_vlan: false + deprecated: false + stats: [] + p4_meta: !!python/object:utils.sai_spec.sai_api_p4_meta.SaiApiP4Meta + tables: + - !!python/object:utils.sai_spec.sai_api_p4_meta.SaiApiP4MetaTable + id: 34229686 + actions: + default: !!python/object:utils.sai_spec.sai_api_p4_meta.SaiApiP4MetaAction + name: default + id: 31749609 + attr_param_id: {} diff --git a/dash-pipeline/SAI/specs/sai_spec.yaml b/dash-pipeline/SAI/specs/sai_spec.yaml index fb31e48f4..8f39c52f7 100644 --- a/dash-pipeline/SAI/specs/sai_spec.yaml +++ b/dash-pipeline/SAI/specs/sai_spec.yaml @@ -41,6 +41,8 @@ object_types: - SAI_OBJECT_TYPE_FLOW_ENTRY_BULK_GET_SESSION - SAI_OBJECT_TYPE_METER_BUCKET_ENTRY - SAI_OBJECT_TYPE_DASH_APPLIANCE +- SAI_OBJECT_TYPE_DASH_TUNNEL_MEMBER +- SAI_OBJECT_TYPE_DASH_TUNNEL_NEXT_HOP object_entries: - !!python/object:utils.sai_spec.sai_struct_entry.SaiStructEntry name: direction_lookup_entry diff --git a/dash-pipeline/SAI/templates/saiapi.cpp.j2 b/dash-pipeline/SAI/templates/saiapi.cpp.j2 index 573aabbe3..b2cd171d0 100644 --- a/dash-pipeline/SAI/templates/saiapi.cpp.j2 +++ b/dash-pipeline/SAI/templates/saiapi.cpp.j2 @@ -59,15 +59,16 @@ static sai_status_t dash_sai_create_{{ table.name }}( matchActionEntry->set_table_id(tableId); - {% if table['keys'] | length== 1 %} - // SAI object table with single P4 key - object ID itself is the P4 table key - // Generate a SAI object ID and fill it as the P4 table key - auto mf = matchActionEntry->add_match(); - mf->set_field_id({{table['keys'][0].id}}); - auto mf_exact = mf->mutable_exact(); - //{{table['keys'][0].field}}SetVal(objId, mf_exact, {{table['keys'][0].bitwidth}}); - {{table['keys'][0].field}}SetVal(static_cast(objId), mf_exact, {{ table['keys'][0].bitwidth }}); - {% else %} + {% 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++) @@ -79,6 +80,7 @@ static sai_status_t dash_sai_create_{{ table.name }}( switch(attr_list[i].id) { {% for key in table['keys'] %} + {% if not key.is_object_key %} case SAI_{{ table.name | upper }}_ATTR_{{ key.name | upper }}: { auto mf = matchActionEntry->add_match(); @@ -138,6 +140,7 @@ static sai_status_t dash_sai_create_{{ table.name }}( {% 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 %} @@ -154,7 +157,6 @@ static sai_status_t dash_sai_create_{{ table.name }}( break; } } - {% endif %} // If there is only one action, simply set it. // Else, search in the attrs. diff --git a/dash-pipeline/SAI/templates/saiapi.h.j2 b/dash-pipeline/SAI/templates/saiapi.h.j2 index 36c942415..ad8c94d10 100644 --- a/dash-pipeline/SAI/templates/saiapi.h.j2 +++ b/dash-pipeline/SAI/templates/saiapi.h.j2 @@ -166,6 +166,8 @@ typedef enum _sai_{{ table.name }}_attr_t * @type {{ sai_attr.type }} {% if sai_attr.isreadonly == 'true' %} * @flags READ_ONLY +{% else if sai_attr.is_create_only == 'true' %} + * @flags CREATE_ONLY {% else %} * @flags CREATE_AND_SET {% endif %} diff --git a/dash-pipeline/SAI/utils/dash_p4/dash_p4_table.py b/dash-pipeline/SAI/utils/dash_p4/dash_p4_table.py index 103a2332c..f931d57cb 100644 --- a/dash-pipeline/SAI/utils/dash_p4/dash_p4_table.py +++ b/dash-pipeline/SAI/utils/dash_p4/dash_p4_table.py @@ -127,7 +127,7 @@ def __table_with_counters(self, program: Dict[str, Any]) -> None: def __parse_table_keys(self, p4rt_table: Dict[str, Any]) -> None: for p4rt_table_key in p4rt_table[MATCH_FIELDS_TAG]: table_key = DashP4TableKey.from_p4rt(p4rt_table_key) - + if self.is_object != "false": table_key.is_entry_key = False @@ -372,9 +372,13 @@ def create_sai_stats(self, sai_api: SaiApi) -> None: def create_sai_attributes(self, sai_api: SaiApi) -> None: # If the table is an object with more one key (table entry id), we need to add all the keys into the attributes. - if self.is_object == "true" and len(self.keys) > 1: - for key in self.keys: - sai_api.attributes.extend(key.to_sai_attribute(self.name, create_only=True)) + if self.is_object == "true": + if len(self.keys) == 1: + self.keys[0].is_object_key = True + elif len(self.keys) > 1: + for key in self.keys: + if not key.is_object_key: + sai_api.attributes.extend(key.to_sai_attribute(self.name)) # Add all the action parameters into the attributes. for attr in self.sai_attributes: diff --git a/dash-pipeline/SAI/utils/dash_p4/dash_p4_table_attribute.py b/dash-pipeline/SAI/utils/dash_p4/dash_p4_table_attribute.py index 79bd50bee..8e3bceef0 100644 --- a/dash-pipeline/SAI/utils/dash_p4/dash_p4_table_attribute.py +++ b/dash-pipeline/SAI/utils/dash_p4/dash_p4_table_attribute.py @@ -15,10 +15,13 @@ def __init__(self): self.bitwidth: int = 0 self.isresourcetype: Optional[str] = None self.isreadonly: Optional[str] = None + self.is_create_only: Optional[str] = None + self.is_mandatory: Optional[str] = None self.object_name: Optional[str] = None self.skipattr: Optional[str] = None self.match_type: str = "" self.validonly: Optional[str] = None + self.is_object_key: bool = False def _parse_sai_table_attribute_annotation( self, p4rt_anno_list: Dict[str, Any] @@ -58,6 +61,10 @@ def _parse_sai_table_attribute_annotation( self.isresourcetype = str(kv["value"]["stringValue"]) elif kv["key"] == "isreadonly": self.isreadonly = str(kv["value"]["stringValue"]) + elif kv["key"] == "create_only": + self.is_create_only = str(kv["value"]["stringValue"]) + elif kv["key"] == "mandatory": + self.is_mandatory = str(kv["value"]["stringValue"]) elif kv["key"] == "objects": self.object_name = str(kv["value"]["stringValue"]) elif kv["key"] == "skipattr": @@ -66,6 +73,8 @@ def _parse_sai_table_attribute_annotation( self.match_type = str(kv["value"]["stringValue"]) elif kv["key"] == "validonly": self.validonly = str(kv["value"]["stringValue"]) + elif kv["key"] == "is_object_key": + self.is_object_key = kv["value"]["stringValue"] == "true" else: raise ValueError("Unknown attr annotation " + kv["key"]) @@ -122,7 +131,7 @@ def to_sai_struct_entry(self, table_name: str) -> List[SaiStructEntry]: return entries - def to_sai_attribute(self, table_name: str, create_only: bool = False, add_action_valid_only_check: bool = False) -> List[SaiAttribute]: + def to_sai_attribute(self, table_name: str, add_action_valid_only_check: bool = False) -> List[SaiAttribute]: name = self.get_sai_name(table_name) description = self.get_sai_description(table_name) @@ -133,13 +142,24 @@ def to_sai_attribute(self, table_name: str, create_only: bool = False, add_actio if self.isreadonly == "true": sai_flags = "READ_ONLY" default_value = None - elif create_only: - sai_flags = "MANDATORY_ON_CREATE | CREATE_ONLY" - default_value = None + elif self.is_create_only == "true": + if self.default == None or self.is_mandatory == "true": + sai_flags = "MANDATORY_ON_CREATE | CREATE_ONLY" + default_value = None + allow_null = False + else: + sai_flags = "CREATE_ONLY" + default_value = self.default + allow_null = False else: - sai_flags = "CREATE_AND_SET" - default_value = self.default + if self.is_mandatory == "true": + sai_flags = "MANDATORY_ON_CREATE | CREATE_AND_SET" + default_value = None + allow_null = False + else: + sai_flags = "CREATE_AND_SET" + default_value = self.default valid_only_checks = [] if add_action_valid_only_check: diff --git a/dash-pipeline/SAI/utils/dash_p4/dash_p4_table_key.py b/dash-pipeline/SAI/utils/dash_p4/dash_p4_table_key.py index a360f1268..d60a646cb 100644 --- a/dash-pipeline/SAI/utils/dash_p4/dash_p4_table_key.py +++ b/dash-pipeline/SAI/utils/dash_p4/dash_p4_table_key.py @@ -11,6 +11,8 @@ class DashP4TableKey(DashP4TableAttribute): def __init__(self): super().__init__() + self.is_create_only: str = "true" + self.is_mandatory: str = "true" self.ip_is_v6_field_id: int = 0 def parse_p4rt(self, p4rt_table_key: Dict[str, Any]) -> None: @@ -55,6 +57,9 @@ def parse_p4rt(self, p4rt_table_key: Dict[str, Any]) -> None: self.set_sai_type(sai_type_info) + # For table keys, we currently require the values to be explicitly set. + self.default = None + return # diff --git a/dash-pipeline/bmv2/README.md b/dash-pipeline/bmv2/README.md index d3949ce11..1d2927a2e 100644 --- a/dash-pipeline/bmv2/README.md +++ b/dash-pipeline/bmv2/README.md @@ -30,7 +30,9 @@ Available tags are: - `default_value`: Override the default value for this key or action parameter. - `isresourcetype`: When set to "true", we generate a corresponding SAI tag in SAI APIs: `@isresourcetype true`. - `objects`: Space separated list of SAI object type this value accepts. When set, we force this value to be a SAI object id, and generate a corresponding SAI tag in SAI APIs: `@objects `. -- `isreadonly`: When set to "true", we generate force this value to be read-only in SAI API using: `@flags READ_ONLY`, otherwise, we generate `@flags CREATE_AND_SET`. +- `isreadonly`: When set to "true", we generate force this value to be read-only in SAI API using: `@flags READ_ONLY`, otherwise, we generate `@flags CREATE_AND_SET`. It cannot be used with `iscreateonly`. +- `iscreateonly`: When set to "true", we generate force this value to be create-only in SAI API using: `@flags CREATE_ONLY`, otherwise, we generate `@flags CREATE_AND_SET`. It cannot be used with `isreadonly`. +- `ismandatory`: When set to "true", we generate force this value to be mandatory in SAI API using: `@flags MANDATORY_ON_CREATE`. It cannot be used with `isreadonly`. - `skipattr`: When set to "true", we skip this attribute in SAI API generation. #### `@SaiCounter`: Counters diff --git a/dash-pipeline/bmv2/dash_metadata.p4 b/dash-pipeline/bmv2/dash_metadata.p4 index 87fbfa597..14ad72ab1 100644 --- a/dash-pipeline/bmv2/dash_metadata.p4 +++ b/dash-pipeline/bmv2/dash_metadata.p4 @@ -326,6 +326,10 @@ struct metadata_t { IPv4Address reverse_tunnel_sip; overlay_rewrite_data_t overlay_data; bit<16> dash_tunnel_id; + bit<32> dash_tunnel_max_member_size; + bit<16> dash_tunnel_member_index; + bit<16> dash_tunnel_member_id; + bit<16> dash_tunnel_next_hop_id; bit<32> meter_class; bit<8> local_region_id; } diff --git a/dash-pipeline/bmv2/stages/tunnel_stage.p4 b/dash-pipeline/bmv2/stages/tunnel_stage.p4 index bf8b878ab..66194c667 100644 --- a/dash-pipeline/bmv2/stages/tunnel_stage.p4 +++ b/dash-pipeline/bmv2/stages/tunnel_stage.p4 @@ -6,18 +6,27 @@ control tunnel_stage( inout metadata_t meta) { action set_tunnel_attrs( - @SaiVal[type="sai_ip_address_t"] - IPv4Address dip, - @SaiVal[type="sai_dash_encapsulation_t", default_value="SAI_DASH_ENCAPSULATION_VXLAN"] - dash_encapsulation_t dash_encapsulation, - bit<24> tunnel_key) { - push_action_static_encap(hdr = hdr, - meta = meta, - encap = dash_encapsulation, - vni = tunnel_key, - underlay_sip = hdr.u0_ipv4.src_addr, - underlay_dip = dip, - overlay_dmac = hdr.u0_ethernet.dst_addr); + @SaiVal[type="sai_dash_encapsulation_t", default_value="SAI_DASH_ENCAPSULATION_VXLAN", create_only="true"] + dash_encapsulation_t dash_encapsulation, + + @SaiVal[create_only="true"] + bit<24> tunnel_key, + + @SaiVal[default_value="1", create_only="true"] + bit<32> max_member_size, + + @SaiVal[type="sai_ip_address_t"] + IPv4Address dip, + + @SaiVal[type="sai_ip_address_t"] + IPv4Address sip) + { + meta.dash_tunnel_max_member_size = max_member_size; + + meta.tunnel_data.dash_encapsulation = dash_encapsulation; + meta.tunnel_data.vni = tunnel_key; + meta.tunnel_data.underlay_sip = sip == 0 ? hdr.u0_ipv4.src_addr : sip; + meta.tunnel_data.underlay_dip = dip; } @SaiTable[name = "dash_tunnel", api = "dash_tunnel", order = 0, isobject="true"] @@ -31,8 +40,98 @@ control tunnel_stage( } } + action select_tunnel_member( + bit<16> dash_tunnel_member_id) + { + meta.dash_tunnel_member_id = dash_tunnel_member_id; + } + + // This table is a helper table that used to select the tunnel member based on the index. + // The entry of this table is created by DASH data plane app, when the tunnel member is created. + @SaiTable[ignored = "true"] + table tunnel_member_select { + key = { + meta.dash_tunnel_member_index : exact @SaiVal[type="sai_object_id_t", is_object_key="true"]; + meta.dash_tunnel_id : exact @SaiVal[type="sai_object_id_t"]; + } + + actions = { + select_tunnel_member; + } + } + + action set_tunnel_member_attrs( + @SaiVal[type="sai_object_id_t", mandatory="true", create_only="true"] bit<16> dash_tunnel_id, + @SaiVal[type="sai_object_id_t", mandatory="true"] bit<16> dash_tunnel_next_hop_id) + { + // dash_tunnel_id in tunnel member must match the metadata + REQUIRES(meta.dash_tunnel_id == dash_tunnel_id); + + meta.dash_tunnel_next_hop_id = dash_tunnel_next_hop_id; + } + + @SaiTable[name = "dash_tunnel_member", api = "dash_tunnel", order = 1, isobject="true"] + table tunnel_member { + key = { + meta.dash_tunnel_member_id : exact @SaiVal[type="sai_object_id_t", is_object_key="true"]; + } + + actions = { + set_tunnel_member_attrs; + } + } + + action set_tunnel_next_hop_attrs( + @SaiVal[type="sai_ip_address_t"] + IPv4Address dip) + { + meta.tunnel_data.underlay_dip = dip == 0 ? meta.tunnel_data.underlay_dip : dip; + } + + @SaiTable[name = "dash_tunnel_next_hop", api = "dash_tunnel", order = 2, isobject="true"] + table tunnel_next_hop { + key = { + meta.dash_tunnel_next_hop_id : exact @SaiVal[type="sai_object_id_t"]; + } + + actions = { + set_tunnel_next_hop_attrs; + } + } + apply { + if (meta.dash_tunnel_id == 0) { + return; + } + tunnel.apply(); + + // If max member size is greater than 1, the tunnel is programmed with multiple members. + if (meta.dash_tunnel_max_member_size > 1) { +#if defined(TARGET_BMV2_V1MODEL) + // Select tunnel member based on the hash of the packet tuples. + hash(meta.dash_tunnel_member_index, HashAlgorithm.crc32, (bit<32>)0, { + meta.dst_ip_addr, + meta.src_ip_addr, + meta.src_l4_port, + meta.dst_l4_port + }, meta.dash_tunnel_max_member_size); +#else + meta.dash_tunnel_member_index = 0; +#endif + tunnel_member_select.apply(); + + tunnel_member.apply(); + tunnel_next_hop.apply(); + } + + push_action_static_encap(hdr = hdr, + meta = meta, + encap = meta.tunnel_data.dash_encapsulation, + vni = meta.tunnel_data.vni, + underlay_sip = meta.tunnel_data.underlay_sip, + underlay_dip = meta.tunnel_data.underlay_dip, + overlay_dmac = hdr.u0_ethernet.dst_addr); } } From 1b3590a1334ccf44654ff40725dd2989ab275753 Mon Sep 17 00:00:00 2001 From: Riff Date: Tue, 22 Oct 2024 19:27:04 -0700 Subject: [PATCH 2/2] [libsai] Make helper function for bulk object create and removal to simplify libsai generation. (#632) All bulk create/removal objects SAI implementation is essentially the same. Hence making some helper functions for simplify the generated libsai. With more changes coming, we should be able to make the libsai templates and generations more maintainable. --- dash-pipeline/SAI/src/dashsai.cpp | 57 +++++++++++++++++++++++ dash-pipeline/SAI/src/dashsai.h | 26 +++++++++++ dash-pipeline/SAI/templates/saiapi.cpp.j2 | 40 +--------------- 3 files changed, 85 insertions(+), 38 deletions(-) diff --git a/dash-pipeline/SAI/src/dashsai.cpp b/dash-pipeline/SAI/src/dashsai.cpp index 6ff0ed3cf..8db034fc4 100644 --- a/dash-pipeline/SAI/src/dashsai.cpp +++ b/dash-pipeline/SAI/src/dashsai.cpp @@ -904,3 +904,60 @@ std::vector DashSai::populateDefaultAttributes( return attrs; } + +sai_status_t DashSai::bulk_create_objects( + _In_ DashSai::sai_create_object_fn create_fn, + _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) +{ + sai_status_t agg_status = SAI_STATUS_SUCCESS; + + for (uint32_t i = 0; i < object_count; i++) + { + object_statuses[i] = create_fn(&object_id[i], switch_id, 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; +} + +sai_status_t DashSai::bulk_remove_objects( + _In_ DashSai::sai_remove_object_fn remove_fn, + _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) +{ + sai_status_t agg_status = SAI_STATUS_SUCCESS; + + for (uint32_t i = 0; i < object_count; i++) + { + object_statuses[i] = remove_fn(object_id[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; +} \ No newline at end of file diff --git a/dash-pipeline/SAI/src/dashsai.h b/dash-pipeline/SAI/src/dashsai.h index adc4cc5f1..1abccf8a5 100644 --- a/dash-pipeline/SAI/src/dashsai.h +++ b/dash-pipeline/SAI/src/dashsai.h @@ -102,6 +102,32 @@ namespace dash _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list); + typedef sai_status_t (*sai_create_object_fn)( + _Out_ sai_object_id_t *object_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + typedef sai_status_t (*sai_remove_object_fn)( + _In_ sai_object_id_t object_id); + + static sai_status_t bulk_create_objects( + _In_ sai_create_object_fn create_fn, + _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); + + static sai_status_t bulk_remove_objects( + _In_ sai_remove_object_fn remove_fn, + _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); + private: // private helper methods static std::shared_ptr parse_p4info( diff --git a/dash-pipeline/SAI/templates/saiapi.cpp.j2 b/dash-pipeline/SAI/templates/saiapi.cpp.j2 index b2cd171d0..93f4dd508 100644 --- a/dash-pipeline/SAI/templates/saiapi.cpp.j2 +++ b/dash-pipeline/SAI/templates/saiapi.cpp.j2 @@ -257,25 +257,7 @@ static sai_status_t dash_sai_create_{{ table.name }}s( _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_{{ table.name }}(&object_id[i], switch_id, 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; + return dash::DashSai::bulk_create_objects(dash_sai_create_{{ table.name }}, switch_id, object_count, attr_count, attr_list, mode, object_id, object_statuses); } static sai_status_t dash_sai_remove_{{ table.name }}( @@ -298,25 +280,7 @@ static sai_status_t dash_sai_remove_{{ table.name }}s( _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_{{ table.name }}(object_id[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; + return dash::DashSai::bulk_remove_objects(dash_sai_remove_{{ table.name }}, object_count, object_id, mode, object_statuses); } static sai_status_t dash_sai_set_{{ table.name }}_attribute (