diff --git a/README.md b/README.md index ddfc33392..41a6b280c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,16 @@ [![DASH-bmv2-CI](https://github.com/sonic-net/DASH/workflows/DASH-BMV2-CI/badge.svg?branch=main)](https://github.com/sonic-net/DASH/actions/workflows/dash-bmv2-ci.yml) [![Spellcheck](https://github.com/sonic-net/DASH/actions/workflows/dash-md-spellcheck.yml/badge.svg)](https://github.com/sonic-net/DASH/actions/workflows/dash-md-spellcheck.yml) -# SONiC-DASH - Disaggregated API for SONiC Hosts - DRAFT/Work in Progress +# SONiC-DASH - Disaggregated API for SONiC Hosts - extending functionality to stateful workloads! +[DASH YouTube Videos](https://www.youtube.com/channel/UCNE3zNwJqcEyLX9ejKrLtUA) -## SONiC-DASH is a new open source project that will "deliver enterprise network performance to critical cloud applications". The project extends functionality to stateful workloads. +## SONiC-DASH is an open source project with the goal to "deliver enterprise network performance to critical cloud applications". The project extends functionality to stateful workloads. DASH has joined the Linux Foundation to participate in the large communities of developers and users contributing there, and also collaborates with OCP. We are developing set of APIs and object models describing network services for the cloud, and will work with all cloud providers and enterprise hybrid clouds to develop further functionality. We believe the DASH program describes a comprehensive set of services that are required by the vast majority of clouds. The goal of DASH is to be specific enough for SMART Programmable Technologies to optimize network performance and leverage commodity **hardware** technology to achieve 10x or even 100x stateful connection performance. The technology has multiple applications such as 1) NIC on a host, 2) a Smart Switch, 3) Network Disaggregation, and 4) high performance Network Appliances. Many technology companies have committed to further developing this new open technology and its community. The best minds and practitioners are actively collaborating to optimize performance of the cloud by extending SONiC to include stateful workloads. + +With the development of a Behavioral Model, we hope to push the boundaries of what P4, PNA, BMv2, and P4 DPDK can do, and contribute to shape the future of networking technology. Together these components provide a cohesive environment for developing, validating, and deploying programmable network devices that can operate consistently across different HW and SW platforms. Behavioral models help in understanding, designing, and analyzing the dynamic behavior of a system, ensuring that it meets its functional requirements. We plan to develop scenarios and test cases that verify the system behaves as expected under various conditions. The Behavioral Model can also be used to simulate a system's behavior before actual implementation, helping to identify potential issues early. Future innovations for in-service software upgrades and ultra-high availability for stateful connections will also be developed with the utmost importance. @@ -18,11 +21,12 @@ Visit the [Documentation table of contents](documentation/README.md) for access For a quick technical deep-dive, please begin with: -1. Peruse the [DASH high level design](documentation/general/dash-high-level-design.md) for an overview of DASH architecture. -1. The [SDN Packet Transforms](documentation/general/sdn-features-packet-transforms.md) document, this facilitates understanding of the program goal and the 7 networking scenarios that Azure has defined. -1. [Program Scale Testing Requirements - Draft](documentation/general/program-scale-testing-requirements-draft.md) for an example of a test to stress DPU/NIC hardware. +1. Peruse the [DASH high level design](documentation/general/dash-high-level-design.md) for an overview of DASH architecture. +2. [SONiC-DASH High Level Design](https://github.com/sonic-net/DASH/blob/main/documentation/general/dash-sonic-hld.md) +3. The [SDN Packet Transforms](documentation/general/sdn-features-packet-transforms.md) document, this facilitates understanding of the program goal and the 7 networking scenarios that Azure has defined. +4. [HERO Test](documentation/general/program-scale-testing-requirements-draft.md) for an example of a test to stress DPU/NIC hardware. -The API and Object Model for VNET<->VNET is in draft; the remaining services will be posted over as we move forward. +The API and Object Model for VNET<->VNET has been posted; the remaining services will be added into the dash-sonic-hld.md as as we move forward. DASH Testing is covered under the [test/](test/README.md) directory and is a work in progress. diff --git a/dash-pipeline/SAI/Makefile b/dash-pipeline/SAI/Makefile index 60d913db4..6acc9ba48 100644 --- a/dash-pipeline/SAI/Makefile +++ b/dash-pipeline/SAI/Makefile @@ -4,7 +4,7 @@ all: copysrc /bmv2/dash_pipeline.bmv2/dash_pipeline_p4rt.json \ --ir /bmv2/dash_pipeline.bmv2/dash_pipeline_ir.json \ --ignore-tables=underlay_mac,eni_meter,slb_decap \ - --sai-spec-dir=/SAI/specs \ + --sai-spec-dir=specs \ dash copysrc: 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 ed071a761..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 @@ -485,18 +487,18 @@ port_extenstion: !!python/object:utils.sai_spec.sai_api_extension.SaiApiExtensio deprecated: false is_vlan: false api_groups: -- !inc '/SAI/specs/dash_acl.yaml' -- !inc '/SAI/specs/dash_direction_lookup.yaml' -- !inc '/SAI/specs/dash_eni.yaml' -- !inc '/SAI/specs/dash_ha.yaml' -- !inc '/SAI/specs/dash_inbound_routing.yaml' -- !inc '/SAI/specs/dash_meter.yaml' -- !inc '/SAI/specs/dash_outbound_ca_to_pa.yaml' -- !inc '/SAI/specs/dash_vnet.yaml' -- !inc '/SAI/specs/dash_outbound_routing.yaml' -- !inc '/SAI/specs/dash_pa_validation.yaml' -- !inc '/SAI/specs/route.yaml' -- !inc '/SAI/specs/dash_vip.yaml' -- !inc '/SAI/specs/dash_tunnel.yaml' -- !inc '/SAI/specs/dash_flow.yaml' -- !inc '/SAI/specs/dash_appliance.yaml' +- !inc 'dash_acl.yaml' +- !inc 'dash_direction_lookup.yaml' +- !inc 'dash_eni.yaml' +- !inc 'dash_ha.yaml' +- !inc 'dash_inbound_routing.yaml' +- !inc 'dash_meter.yaml' +- !inc 'dash_outbound_ca_to_pa.yaml' +- !inc 'dash_vnet.yaml' +- !inc 'dash_outbound_routing.yaml' +- !inc 'dash_pa_validation.yaml' +- !inc 'route.yaml' +- !inc 'dash_vip.yaml' +- !inc 'dash_tunnel.yaml' +- !inc 'dash_flow.yaml' +- !inc 'dash_appliance.yaml' 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 573aabbe3..93f4dd508 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. @@ -255,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 }}( @@ -296,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 ( 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/SAI/utils/sai_spec/sai_spec.py b/dash-pipeline/SAI/utils/sai_spec/sai_spec.py index e0ccc4108..eef270ac5 100644 --- a/dash-pipeline/SAI/utils/sai_spec/sai_spec.py +++ b/dash-pipeline/SAI/utils/sai_spec/sai_spec.py @@ -39,7 +39,7 @@ def serialize(self, spec_dir: str): f.write(yaml.dump(api_group, indent=2, sort_keys=False)) yaml_inc_files.append( - yaml_include.Data(urlpath=sai_api_group_spec_file_path) + yaml_include.Data(urlpath=os.path.relpath(sai_api_group_spec_file_path, spec_dir)) ) api_groups = self.api_groups 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); } }