diff --git a/dash-pipeline/SAI/templates/saiapi.h.j2 b/dash-pipeline/SAI/templates/saiapi.h.j2 index 36c942415..bafb639c5 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.iscreateonly == '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 67a7c5424..1d03687fe 100644 --- a/dash-pipeline/SAI/utils/dash_p4/dash_p4_table.py +++ b/dash-pipeline/SAI/utils/dash_p4/dash_p4_table.py @@ -127,6 +127,9 @@ 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) + + # For table keys, we currently require the values to be explicitly set. + table_key.default = None if self.is_object != "false": table_key.is_entry_key = False @@ -378,7 +381,7 @@ def create_sai_attributes(self, sai_api: SaiApi) -> None: 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, create_only=True)) + 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 86699cb4e..bdde67de3 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,6 +15,7 @@ def __init__(self): self.bitwidth: int = 0 self.isresourcetype: Optional[str] = None self.isreadonly: Optional[str] = None + self.iscreateonly: Optional[str] = None self.object_name: Optional[str] = None self.skipattr: Optional[str] = None self.match_type: str = "" @@ -59,6 +60,8 @@ 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"] == "iscreateonly": + self.iscreateonly = str(kv["value"]["stringValue"]) elif kv["key"] == "objects": self.object_name = str(kv["value"]["stringValue"]) elif kv["key"] == "skipattr": @@ -125,7 +128,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) @@ -136,9 +139,13 @@ 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.iscreateonly == "true": + if self.default == None: + sai_flags = "MANDATORY_ON_CREATE | CREATE_ONLY" + else: + sai_flags = "CREATE_ONLY" + + default_value = self.default allow_null = False else: sai_flags = "CREATE_AND_SET" 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..758497dd3 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,7 @@ class DashP4TableKey(DashP4TableAttribute): def __init__(self): super().__init__() + self.iscreateonly: str = "true" self.ip_is_v6_field_id: int = 0 def parse_p4rt(self, p4rt_table_key: Dict[str, Any]) -> None: diff --git a/dash-pipeline/bmv2/README.md b/dash-pipeline/bmv2/README.md index d3949ce11..9b0baac57 100644 --- a/dash-pipeline/bmv2/README.md +++ b/dash-pipeline/bmv2/README.md @@ -30,7 +30,8 @@ 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`. - `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 2defa9610..62668b23b 100644 --- a/dash-pipeline/bmv2/dash_metadata.p4 +++ b/dash-pipeline/bmv2/dash_metadata.p4 @@ -322,6 +322,7 @@ struct metadata_t { encap_data_t tunnel_data; 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; diff --git a/dash-pipeline/bmv2/stages/tunnel_stage.p4 b/dash-pipeline/bmv2/stages/tunnel_stage.p4 index 23578e63a..c9d824d1a 100644 --- a/dash-pipeline/bmv2/stages/tunnel_stage.p4 +++ b/dash-pipeline/bmv2/stages/tunnel_stage.p4 @@ -11,12 +11,17 @@ control tunnel_stage( bit<24> tunnel_key, + @SaiVal[default_value="1", iscreateonly="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; @@ -100,12 +105,19 @@ control tunnel_stage( tunnel.apply(); - if (meta.tunnel_data.underlay_dip == 0) { - // If underlay_dip is not set by the tunnel, we are using the tunnel as ECMP group. - - // TODO: Calculate based on packet and tunnel member size. - // Currently, we will have to use 0 here, because we don't know how many tunnel members we will have. + // 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();