Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[sai-gen] Simplify type solving, move enum type solving to use Sai attribute, add default value in Sai attribute #475

Merged
merged 4 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 131 additions & 76 deletions dash-pipeline/SAI/sai_api_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,89 +80,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):
Expand All @@ -178,6 +228,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):
'''
Expand Down Expand Up @@ -258,6 +309,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':
Expand All @@ -269,7 +322,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
Expand Down Expand Up @@ -299,6 +355,7 @@ class SAIEnum(SAIObject):
'''
def __init__(self):
super().__init__()
self.bitwidth = 0
self.members = []

def parse_p4rt(self, p4rt_enum):
Expand All @@ -317,9 +374,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):
Expand Down Expand Up @@ -372,16 +438,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"
Expand Down Expand Up @@ -433,7 +492,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)

Expand All @@ -445,7 +503,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 = []

Expand All @@ -459,19 +516,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"
Expand Down
5 changes: 3 additions & 2 deletions dash-pipeline/bmv2/dash_outbound.p4
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -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) {
Expand Down