From 256b533beefdd06e325a594ff3b9020349077365 Mon Sep 17 00:00:00 2001 From: Riff Date: Tue, 12 Dec 2023 08:15:59 -0800 Subject: [PATCH] [sai-gen] Simplify type solving, move enum type solving to use Sai attribute, add default value in Sai attribute (#475) With these changes, we will be able to specify the enum types and default value for any table key and action parameter as below: ``` @Sai[type="sai_dash_encapsulation_t", default_value="SAI_DASH_ENCAPSULATION_VXLAN"] dash_encapsulation_t dash_encapsulation, ``` This removes the dependency on the variable name which has to match with the type. This makes the generated SAI matches what is in opencompute SAI. --- dash-pipeline/SAI/sai_api_gen.py | 207 ++++++++++++++++++---------- dash-pipeline/bmv2/dash_outbound.p4 | 5 +- 2 files changed, 134 insertions(+), 78 deletions(-) diff --git a/dash-pipeline/SAI/sai_api_gen.py b/dash-pipeline/SAI/sai_api_gen.py index 0eda8fd5a..581b5c32c 100755 --- a/dash-pipeline/SAI/sai_api_gen.py +++ b/dash-pipeline/SAI/sai_api_gen.py @@ -81,89 +81,139 @@ def parse(self, p4rt_value, *args, **kwargs): # |- SAIAPITableAction <-------| : Information of a single P4 table action defined used by the table. # |- SAIAPITableActionParam -| : Information of a single P4 table action parameter used by the action. # +class SAITypeInfo: + def __init__(self, name, field_func_prefix, default = None, is_enum = False): + self.name = name + self.field_func_prefix = field_func_prefix + self.default = default + self.is_enum = is_enum + class SAITypeSolver: - sai_type_to_field = { - 'bool': 'booldata', - 'sai_uint8_t': 'u8', - 'sai_object_id_t': 'u16', - 'sai_uint16_t': 'u16', - 'sai_ip_address_t': 'ipaddr', - 'sai_ip_addr_family_t': 'u32', - 'sai_uint32_t': 'u32', - 'sai_uint64_t': 'u64', - 'sai_mac_t': 'mac', - 'sai_ip_prefix_list_t': 'ipprefixlist' + sai_type_info_registry = { + "bool": SAITypeInfo("bool", "booldata"), + "sai_uint8_t": SAITypeInfo("sai_uint8_t", "u8"), + "sai_object_id_t": SAITypeInfo("sai_object_id_t", "u16"), + "sai_uint16_t": SAITypeInfo("sai_uint16_t", "u16"), + "sai_ip_address_t": SAITypeInfo("sai_ip_address_t", "ipaddr"), + "sai_ip_addr_family_t": SAITypeInfo("sai_ip_addr_family_t", "u32"), + "sai_uint32_t": SAITypeInfo("sai_uint32_t", "u32"), + "sai_uint64_t": SAITypeInfo("sai_uint64_t", "u64"), + "sai_mac_t": SAITypeInfo("sai_mac_t", "mac"), + "sai_ip_prefix_t": SAITypeInfo("sai_ip_prefix_t", "ipPrefix"), + "sai_u8_list_t": SAITypeInfo("sai_u8_list_t", "u8list"), + "sai_u16_list_t": SAITypeInfo("sai_u16_list_t", "u16list"), + "sai_u32_list_t": SAITypeInfo("sai_u32_list_t", "u32list"), + "sai_ip_prefix_list_t": SAITypeInfo("sai_ip_prefix_list_t", "ipprefixlist"), + "sai_u8_range_list_t": SAITypeInfo("sai_u8_range_list_t", "u8rangelist"), + "sai_u16_range_list_t": SAITypeInfo("sai_u16_range_list_t", "u16rangelist"), + "sai_u32_range_list_t": SAITypeInfo("sai_u32_range_list_t", "u32rangelist"), + "sai_u64_range_list_t": SAITypeInfo("sai_u64_range_list_t", "u64rangelist"), + "sai_ipaddr_range_list_t": SAITypeInfo("sai_ipaddr_range_list_t", "ipaddrrangelist"), } @staticmethod - def get_sai_default_field_from_type(sai_type): - return SAITypeSolver.sai_type_to_field[sai_type] + def register_sai_type(name, field_func_prefix, default = None, is_enum = False): + SAITypeSolver.sai_type_info_registry[name] = SAITypeInfo(name, field_func_prefix=field_func_prefix, default=default, is_enum=is_enum) @staticmethod - def get_sai_key_type(key_size, key_header, key_field): - if key_size == 1: - return 'bool', "booldata" - elif key_size <= 8: - return 'sai_uint8_t', "u8" - elif key_size == 16 and ('_id' in key_field): - return 'sai_object_id_t', "u16" - elif key_size <= 16: - return 'sai_uint16_t', "u16" - elif key_size == 32 and ('ip_addr_family' in key_field): - return 'sai_ip_addr_family_t', "u32" - elif key_size == 32 and ('addr' in key_field or 'ip' in key_header): - return 'sai_ip_address_t', "ipaddr" - elif key_size == 32 and ('_id' in key_field): - return 'sai_object_id_t', "u32" - elif key_size <= 32: - return 'sai_uint32_t', "u32" - elif key_size == 48 and ('addr' in key_field or 'mac' in key_header): - return 'sai_mac_t', "mac" - elif key_size <= 64: - return 'sai_uint64_t', "u64" - elif key_size == 128: - return 'sai_ip_address_t', "ipaddr" + def get_sai_type(sai_type): + return SAITypeSolver.sai_type_info_registry[sai_type] + + @staticmethod + def get_object_sai_type(object_size, object_parent_name, object_name): + sai_type_name = "" + + if object_size == 1: + sai_type_name = 'bool' + elif object_size <= 8: + sai_type_name = 'sai_uint8_t' + elif object_size == 16 and ('_id' in object_name): + sai_type_name = 'sai_object_id_t' + elif object_size <= 16: + sai_type_name = 'sai_uint16_t' + elif object_size == 32 and ('ip_addr_family' in object_name): + sai_type_name = 'sai_ip_addr_family_t' + elif object_size == 32 and ('addr' in object_name or 'ip' in object_parent_name): + sai_type_name = 'sai_ip_address_t' + elif object_size == 32 and ('_id' in object_name): + sai_type_name = 'sai_object_id_t' + elif object_size <= 32: + sai_type_name = 'sai_uint32_t' + elif object_size == 48 and ('addr' in object_name or 'mac' in object_parent_name): + sai_type_name = 'sai_mac_t' + elif object_size <= 64: + sai_type_name = 'sai_uint64_t' + elif object_size == 128: + sai_type_name = 'sai_ip_address_t' else: - raise ValueError(f'key_size={key_size} is not supported') + raise ValueError(f'key_size={object_size} is not supported') + + return SAITypeSolver.get_sai_type(sai_type_name) @staticmethod - def get_sai_lpm_type(key_size, key_header, key_field): - if key_size == 32 and ('addr' in key_field or 'ip' in key_header): - return 'sai_ip_prefix_t', 'ipPrefix' - elif key_size == 128 and ('addr' in key_field or 'ip' in key_header): - return 'sai_ip_prefix_t', 'ipPrefix' - raise ValueError(f'key_size={key_size}, key_header={key_header}, and key_field={key_field} is not supported') + def get_match_key_sai_type(match_type, key_size, key_parent_name, key_name): + if match_type == 'exact' or match_type == 'optional' or match_type == 'ternary': + return SAITypeSolver.get_object_sai_type(key_size, key_parent_name, key_name) + elif match_type == 'lpm': + return SAITypeSolver.__get_lpm_match_key_sai_type(key_size, key_parent_name, key_name) + elif match_type == 'list': + return SAITypeSolver.__get_list_match_key_sai_type(key_size, key_parent_name, key_name) + elif match_type == 'range_list': + return SAITypeSolver.__get_range_list_sai_type(key_size, key_parent_name, key_name) + else: + raise ValueError(f"match_type={match_type} is not supported") @staticmethod - def get_sai_list_type(key_size, key_header, key_field): + def __get_lpm_match_key_sai_type(key_size, key_parent_name, key_name): + sai_type_name = "" + + if key_size == 32 and ('addr' in key_name or 'ip' in key_parent_name): + sai_type_name = 'sai_ip_prefix_t' + elif key_size == 128 and ('addr' in key_name or 'ip' in key_parent_name): + sai_type_name = 'sai_ip_prefix_t' + else: + raise ValueError(f'key_size={key_size}, key_header={key_parent_name}, and key_field={key_name} is not supported') + + return SAITypeSolver.get_sai_type(sai_type_name) + + @staticmethod + def __get_list_match_key_sai_type(key_size, key_header, key_field): + sai_type_name = "" + if key_size <= 8: - return 'sai_u8_list_t', "u8list" + sai_type_name = 'sai_u8_list_t' elif key_size <= 16: - return 'sai_u16_list_t', "u16list" + sai_type_name = 'sai_u16_list_t' elif key_size == 32 and ('addr' in key_field or 'ip' in key_header): - return 'sai_ip_prefix_list_t', "ipprefixlist" + sai_type_name = 'sai_ip_prefix_list_t' elif key_size <= 32: - return 'sai_u32_list_t', "u32list" + sai_type_name = 'sai_u32_list_t' elif key_size == 128 and ('addr' in key_field or 'ip' in key_header): - return 'sai_ip_prefix_list_t', "ipprefixlist" + sai_type_name = 'sai_ip_prefix_list_t' else: raise ValueError(f'key_size={key_size} is not supported') + return SAITypeSolver.get_sai_type(sai_type_name) + @staticmethod - def get_sai_range_list_type(key_size, key_header, key_field): + def __get_range_list_sai_type(key_size, key_header, key_field): + sai_type_name = "" + if key_size <= 8: - return 'sai_u8_range_list_t', 'u8rangelist' + sai_type_name = 'sai_u8_range_list_t' elif key_size <= 16: - return 'sai_u16_range_list_t', 'u16rangelist' + sai_type_name = 'sai_u16_range_list_t' elif key_size == 32 and ('addr' in key_field or 'ip' in key_header): - return 'sai_ipaddr_range_list_t', 'ipaddrrangelist' + sai_type_name = 'sai_ipaddr_range_list_t' elif key_size <= 32: - return 'sai_u32_range_list_t', 'u32rangelist' + sai_type_name = 'sai_u32_range_list_t' elif key_size <= 64: - return 'sai_u64_range_list_t', 'u64rangelist' + sai_type_name = 'sai_u64_range_list_t' else: raise ValueError(f'key_size={key_size} is not supported') + return SAITypeSolver.get_sai_type(sai_type_name) + class SAIObject: def __init__(self): @@ -179,6 +229,7 @@ def __init__(self): self.object_name = None self.skipattr = None self.field = None + self.default = None def parse_preamble_if_exists(self, p4rt_object): ''' @@ -261,6 +312,8 @@ def _parse_sai_object_annotation(self, p4rt_anno_list): for kv in anno[KV_PAIR_LIST_TAG][KV_PAIRS_TAG]: if kv['key'] == 'type': self.type = kv['value']['stringValue'] + elif kv['key'] == 'default_value': # "default" is a reserved keyword and cannot be used. + self.default = kv['value']['stringValue'] elif kv['key'] == 'isresourcetype': self.isresourcetype = kv['value']['stringValue'] elif kv['key'] == 'isreadonly': @@ -272,7 +325,10 @@ def _parse_sai_object_annotation(self, p4rt_anno_list): else: raise ValueError("Unknown attr annotation " + kv['key']) - self.field = SAITypeSolver.get_sai_default_field_from_type(self.type) + sai_type_info = SAITypeSolver.get_sai_type(self.type) + self.field = sai_type_info.field_func_prefix + if self.default == None and sai_type_info.is_enum: + self.default = sai_type_info.default @sai_parser_from_p4rt @@ -302,6 +358,7 @@ class SAIEnum(SAIObject): ''' def __init__(self): super().__init__() + self.bitwidth = 0 self.members = [] def parse_p4rt(self, p4rt_enum): @@ -320,9 +377,18 @@ def parse_p4rt(self, p4rt_enum): } ''' print("Parsing enum: " + self.name) + self.name = self.name[:-2] + self.bitwidth = p4rt_enum['underlyingType'][BITWIDTH_TAG] self.members = [SAIEnumMember.from_p4rt(enum_member['value'], name = enum_member['name']) for enum_member in p4rt_enum[MEMBERS_TAG]] + # Register enum type info. + SAITypeSolver.register_sai_type( + 'sai_' + self.name + '_t', + "s32", + default = 'SAI_' + self.name.upper() + '_INVALID', + is_enum = True) + @sai_parser_from_p4rt class SAIAPITableKey(SAIObject): @@ -375,16 +441,9 @@ def parse_p4rt(self, p4rt_table_key, ip_is_v6_key_ids): if STRUCTURED_ANNOTATIONS_TAG in p4rt_table_key: self._parse_sai_object_annotation(p4rt_table_key) else: - if self.match_type == 'exact' or self.match_type == 'optional' or self.match_type == 'ternary': - self.type, self.field = SAITypeSolver.get_sai_key_type(self.bitwidth, key_header, key_field) - elif self.match_type == 'lpm': - self.type, self.field = SAITypeSolver.get_sai_lpm_type(self.bitwidth, key_header, key_field) - elif self.match_type == 'list': - self.type, self.field = SAITypeSolver.get_sai_list_type(self.bitwidth, key_header, key_field) - elif self.match_type == 'range_list': - self.type, self.field = SAITypeSolver.get_sai_range_list_type(self.bitwidth, key_header, key_field) - else: - raise ValueError(f"match_type={self.match_type} is not supported") + sai_type_info = SAITypeSolver.get_match_key_sai_type(self.match_type, self.bitwidth, key_header, key_field) + self.type = sai_type_info.name + self.field = sai_type_info.field_func_prefix # If *_is_v6 key is present, save its id. ip_is_v6_key_name = self.sai_key_name + "_is_v6" @@ -436,7 +495,6 @@ def parse_action_params(self, p4rt_table_action, sai_enums): # Parse all params. for p in p4rt_table_action[PARAMS_TAG]: - param_name = p[NAME_TAG] param = SAIAPITableActionParam.from_p4rt(p, sai_enums = sai_enums, ip_is_v6_param_ids = ip_is_v6_param_ids) self.params.append(param) @@ -448,7 +506,6 @@ class SAIAPITableActionParam(SAIObject): def __init__(self): super().__init__() self.bitwidth = 0 - self.default = None self.ip_is_v6_field_id = 0 self.param_actions = [] @@ -462,19 +519,17 @@ def parse_p4rt(self, p4rt_table_action_param, sai_enums, ip_is_v6_param_ids): ''' self.id = p4rt_table_action_param['id'] self.name = p4rt_table_action_param[NAME_TAG] + self.bitwidth = p4rt_table_action_param[BITWIDTH_TAG] #print("Parsing table action param: " + self.name) if STRUCTURED_ANNOTATIONS_TAG in p4rt_table_action_param: self._parse_sai_object_annotation(p4rt_table_action_param) else: - self.type, self.field = SAITypeSolver.get_sai_key_type(int(p4rt_table_action_param[BITWIDTH_TAG]), p4rt_table_action_param[NAME_TAG], p4rt_table_action_param[NAME_TAG]) - for sai_enum in sai_enums: - if self.name == sai_enum.name: - self.type = 'sai_' + self.name + '_t' - self.field = 's32' - self.default = 'SAI_' + self.name.upper() + '_INVALID' - - self.bitwidth = p4rt_table_action_param[BITWIDTH_TAG] + sai_type_info = SAITypeSolver.get_object_sai_type(self.bitwidth, self.name, self.name) + print("Parsing table action param: " + self.name + ", type: " + sai_type_info.name, ", is_enum: " + str(sai_type_info.is_enum)) + self.type, self.field = sai_type_info.name, sai_type_info.field_func_prefix + if sai_type_info.is_enum: + self.default = sai_type_info.default # If *_is_v6 key is present, save its id. ip_is_v6_param_name = self.name + "_is_v6" diff --git a/dash-pipeline/bmv2/dash_outbound.p4 b/dash-pipeline/bmv2/dash_outbound.p4 index cc9658696..76e7ca5c0 100644 --- a/dash-pipeline/bmv2/dash_outbound.p4 +++ b/dash-pipeline/bmv2/dash_outbound.p4 @@ -54,6 +54,7 @@ control outbound(inout headers_t hdr, IPv4ORv6Address underlay_dip, bit<1> underlay_sip_is_v6, IPv4ORv6Address underlay_sip, + @Sai[type="sai_dash_encapsulation_t", default_value="SAI_DASH_ENCAPSULATION_VXLAN"] dash_encapsulation_t dash_encapsulation, bit<24> tunnel_key, bit<1> meter_policy_en, @@ -119,7 +120,7 @@ control outbound(inout headers_t hdr, } action set_tunnel(IPv4Address underlay_dip, - dash_encapsulation_t dash_encapsulation, + @Sai[type="sai_dash_encapsulation_t"] dash_encapsulation_t dash_encapsulation, bit<16> meter_class, bit<1> meter_class_override) { meta.encap_data.underlay_dip = underlay_dip; @@ -146,7 +147,7 @@ control outbound(inout headers_t hdr, action set_private_link_mapping(IPv4Address underlay_dip, IPv6Address overlay_sip, IPv6Address overlay_dip, - dash_encapsulation_t dash_encapsulation, + @Sai[type="sai_dash_encapsulation_t"] dash_encapsulation_t dash_encapsulation, bit<24> tunnel_key, bit<16> meter_class, bit<1> meter_class_override) {