From 6b24497bfb492765b6113c5c429dd79f46c9be5a Mon Sep 17 00:00:00 2001 From: r12f Date: Sun, 2 Jun 2024 01:45:38 +0000 Subject: [PATCH] Add P4 meta in SAI spec for lib generation, support merge SAI spec. --- .../SAI/utils/dash_p4/dash_p4_counter.py | 4 +-- .../SAI/utils/dash_p4/dash_p4_table.py | 29 +++++++++++++++---- .../utils/dash_p4/dash_p4_table_attribute.py | 12 ++++---- .../SAI/utils/dash_p4/dash_p4_table_group.py | 25 +++++++++------- .../SAI/utils/dash_p4/dash_p4_table_key.py | 4 +-- .../SAI/utils/dash_p4/dash_sai_extensions.py | 4 ++- dash-pipeline/SAI/utils/sai_spec/__init__.py | 1 + dash-pipeline/SAI/utils/sai_spec/sai_api.py | 6 ++++ 8 files changed, 58 insertions(+), 27 deletions(-) diff --git a/dash-pipeline/SAI/utils/dash_p4/dash_p4_counter.py b/dash-pipeline/SAI/utils/dash_p4/dash_p4_counter.py index 88ef46b62..bb20c8bfa 100644 --- a/dash-pipeline/SAI/utils/dash_p4/dash_p4_counter.py +++ b/dash-pipeline/SAI/utils/dash_p4/dash_p4_counter.py @@ -147,13 +147,13 @@ def generate_counter_sai_attributes(self) -> "Iterator[DashP4Counter]": # # Functions for generating SAI specs. # - def _get_sai_name(self, table_name: str) -> str: + def get_sai_name(self, table_name: str) -> str: if self.attr_type == "stats": return f"SAI_{table_name.upper()}_STAT_{self.name.upper()}" return f"SAI_{table_name.upper()}_{self.name.upper()}" - def _get_sai_description(self, table_name: str): + def get_sai_description(self, table_name: str): if self.attr_type == "stats": return f"DASH {table_name.upper()} {self.name.upper()} stat count" 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 1fff37500..6b4a2ff71 100644 --- a/dash-pipeline/SAI/utils/dash_p4/dash_p4_table.py +++ b/dash-pipeline/SAI/utils/dash_p4/dash_p4_table.py @@ -5,7 +5,7 @@ from .dash_p4_table_action_param import * from .dash_p4_table_key import * from .dash_p4_table_action import * -from ..sai_spec import SaiApi, SaiStruct, SaiEnum, SaiEnumMember, SaiAttribute +from ..sai_spec import SaiApi, SaiStruct, SaiEnum, SaiEnumMember, SaiAttribute, SaiApiP4MetaAction, SaiApiP4MetaTable @dash_p4rt_parser @@ -264,6 +264,7 @@ def __build_sai_attributes_after_parsing(self): # def to_sai(self) -> SaiApi: sai_api = SaiApi(self.name, "", self.is_object != "false") + sai_api.p4_meta.tables.append(SaiApiP4MetaTable(self.id)) self.create_sai_action_enum(sai_api) self.create_sai_structs(sai_api) @@ -276,19 +277,36 @@ def create_sai_action_enum(self, sai_api: SaiApi) -> None: # If the table represents an SAI object, it should not have an action enum. # If the table has only 1 action, we don't need to create the action enum. if len(self.actions) <= 1 and self.is_object != "false": + # We still need to create the p4 meta action here for generating default action code in libsai. + if len(self.actions) == 1: + sai_api.p4_meta.tables[0].actions["default"] = SaiApiP4MetaAction("default", self.actions[0].id) return action_enum_member_value = 0 action_enum_members: List[SaiEnumMember] = [] for action in self.actions: + action_enum_member_name = f"SAI_{self.name.upper()}_ACTION_{action.name.upper()}" + action_enum_members.append( SaiEnumMember( - name=f"SAI_{self.name.upper()}_ACTION_{action.name.upper()}", + name=action_enum_member_name, description="", value=str(action_enum_member_value), ) ) + p4_meta_action = SaiApiP4MetaAction( + name=action_enum_member_name, + id=action.id, + ) + + for action_param in action.params: + p4_meta_action.attr_param_id[action_param.get_sai_name(self.name)] = action_param.id + + sai_api.p4_meta.tables[0].actions[action_enum_member_name] = p4_meta_action + + action_enum_member_value += 1 + action_enum_type_name = f"sai_{self.name.lower()}_action_t" action_enum = SaiEnum( @@ -307,6 +325,7 @@ def create_sai_action_enum(self, sai_api: SaiApi) -> None: ) sai_api.attributes.append(sai_attr_action) + def create_sai_structs(self, sai_api: SaiApi) -> None: # The entry struct is only needed for non-object tables. if self.is_object != "false": @@ -329,9 +348,9 @@ def create_sai_stats(self, sai_api: SaiApi) -> None: ] def create_sai_attributes(self, sai_api: SaiApi) -> None: - sai_api.attributes.extend( - [attr.to_sai_attribute(self.name) for attr in self.sai_attributes if attr.skipattr != "true"] - ) + for attr in self.sai_attributes: + if attr.skipattr != "true": + sai_api.attributes.append(attr.to_sai_attribute(self.name)) # If the table has an counter attached, we need to create a counter attribute for it. # The counter attribute only counts that packets that hits any entry, but not the packet that misses all entries. 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 9563f3d51..74487d5c1 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 @@ -95,8 +95,8 @@ def set_sai_type(self, sai_type_info: SAITypeInfo) -> None: # Functions for generating SAI specs. # def to_sai_struct_entry(self, table_name: str) -> SaiStructEntry: - name = self._get_sai_name(table_name) - description = self._get_sai_description(table_name) + name = self.get_sai_name(table_name) + description = self.get_sai_description(table_name) object_name = f"SAI_OBJECT_TYPE_{self.object_name.upper()}" if self.object_name else None return SaiStructEntry( @@ -108,8 +108,8 @@ def to_sai_struct_entry(self, table_name: str) -> SaiStructEntry: ) def to_sai_attribute(self, table_name: str) -> SaiAttribute: - name = self._get_sai_name(table_name) - description = self._get_sai_description(table_name) + name = self.get_sai_name(table_name) + description = self.get_sai_description(table_name) default_value = None if self.isreadonly == "true" else self.default object_name = f"SAI_OBJECT_TYPE_{self.object_name.upper()}" if self.object_name else None @@ -129,8 +129,8 @@ def to_sai_attribute(self, table_name: str) -> SaiAttribute: valid_only = self.validonly, ) - def _get_sai_name(self, table_name: str) -> str: + def get_sai_name(self, table_name: str) -> str: return f"SAI_{table_name.upper()}_{self.name.upper()}" - def _get_sai_description(self, table_name: str): + def get_sai_description(self, table_name: str): return f"Action parameter {self.name.upper()}" \ No newline at end of file diff --git a/dash-pipeline/SAI/utils/dash_p4/dash_p4_table_group.py b/dash-pipeline/SAI/utils/dash_p4/dash_p4_table_group.py index c6db8c07d..e2c232a8c 100644 --- a/dash-pipeline/SAI/utils/dash_p4/dash_p4_table_group.py +++ b/dash-pipeline/SAI/utils/dash_p4/dash_p4_table_group.py @@ -1,7 +1,7 @@ from typing import List, Optional from .common import * from .dash_p4_table import DashP4Table -from ..sai_spec import SaiApiGroup +from ..sai_spec import SaiApiGroup, SaiApi class DashP4TableGroup(DashP4Object): @@ -25,20 +25,23 @@ def add_table(self, table: DashP4Table) -> None: self.tables.append(table) def post_parsing_process(self, all_table_names: List[str]) -> None: - self.__ignore_duplicated_tables_in_headers() - for table in self.tables: table.post_parsing_process(all_table_names) - - def __ignore_duplicated_tables_in_headers(self) -> None: - table_names = set() + + def to_sai(self) -> SaiApiGroup: + sai_api_list: List[SaiApi] = [] + sai_api_map: Dict[str, SaiApi] = {} for table in self.tables: - if table.name in table_names: + sai_api = table.to_sai() + + if table.name in sai_api_map: table.ignored_in_header = True - table_names.add(table.name) + sai_api_map[table.name].p4_meta.tables.extend(sai_api.p4_meta.tables) + else: + sai_api_map[table.name] = sai_api + sai_api_list.append(sai_api) - def to_sai(self) -> SaiApiGroup: sai_api_group = SaiApiGroup(self.app_name, "") - sai_api_group.sai_apis = [table.to_sai() for table in self.tables if not table.ignored_in_header] - return sai_api_group \ No newline at end of file + sai_api_group.sai_apis = sai_api_list + return sai_api_group 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 ec2c26f20..6dc1c58d4 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 @@ -61,11 +61,11 @@ def parse_p4rt(self, p4rt_table_key: Dict[str, Any]) -> None: # # Functions for generating SAI specs. # - def _get_sai_name(self, table_name: str) -> str: + def get_sai_name(self, table_name: str) -> str: if self.is_entry_key: return self.name return f"SAI_{table_name.upper()}_{self.name.upper()}" - def _get_sai_description(self, table_name: str): + def get_sai_description(self, table_name: str): return f"{self.match_type.capitalize()} matched key {self.name}" \ No newline at end of file diff --git a/dash-pipeline/SAI/utils/dash_p4/dash_sai_extensions.py b/dash-pipeline/SAI/utils/dash_p4/dash_sai_extensions.py index 19ae5bf26..897d99fbd 100644 --- a/dash-pipeline/SAI/utils/dash_p4/dash_sai_extensions.py +++ b/dash-pipeline/SAI/utils/dash_p4/dash_sai_extensions.py @@ -119,12 +119,14 @@ def post_parsing_process(self) -> None: # def to_sai(self) -> SaiSpec: sai_spec = SaiSpec() + sai_spec.api_groups = [api_group.to_sai() for api_group in self.table_groups] + self.create_sai_api_types(sai_spec) self.create_sai_object_types(sai_spec) self.create_sai_object_entries(sai_spec) self.create_sai_enums(sai_spec) self.create_sai_port_counters(sai_spec.port_extenstion) - sai_spec.api_groups = [api_group.to_sai() for api_group in self.table_groups] + return sai_spec def create_sai_api_types(self, sai_spec: SaiSpec): diff --git a/dash-pipeline/SAI/utils/sai_spec/__init__.py b/dash-pipeline/SAI/utils/sai_spec/__init__.py index e79348a72..3542b4756 100644 --- a/dash-pipeline/SAI/utils/sai_spec/__init__.py +++ b/dash-pipeline/SAI/utils/sai_spec/__init__.py @@ -7,3 +7,4 @@ from .sai_struct import SaiStruct from .sai_struct_entry import SaiStructEntry from .sai_attribute import SaiAttribute +from .sai_api_p4_meta import SaiApiP4Meta, SaiApiP4MetaTable, SaiApiP4MetaAction \ No newline at end of file diff --git a/dash-pipeline/SAI/utils/sai_spec/sai_api.py b/dash-pipeline/SAI/utils/sai_spec/sai_api.py index 494c11bc5..19f10f405 100644 --- a/dash-pipeline/SAI/utils/sai_spec/sai_api.py +++ b/dash-pipeline/SAI/utils/sai_spec/sai_api.py @@ -3,6 +3,7 @@ from .sai_attribute import SaiAttribute from .sai_enum import SaiEnum from .sai_struct import SaiStruct +from .sai_api_p4_meta import SaiApiP4Meta from . import sai_spec_utils @@ -18,6 +19,7 @@ def __init__(self, name: str, description: str, is_object: bool = False): self.structs: List[SaiStruct] = [] self.attributes: List[SaiAttribute] = [] self.stats: List[SaiAttribute] = [] + self.p4_meta: SaiApiP4Meta = SaiApiP4Meta() def merge(self, other: "SaiCommon"): super().merge(other) @@ -27,3 +29,7 @@ def merge(self, other: "SaiCommon"): sai_spec_utils.merge_sai_common_lists(self.structs, other.structs) sai_spec_utils.merge_sai_common_lists(self.attributes, other.attributes) sai_spec_utils.merge_sai_common_lists(self.stats, other.stats) + + # The P4 meta can be merged by replacing the old one, since it doesn't affect the ABI, + # and the new one is always more up-to-date. + self.p4_meta = other.p4_meta