From 8b8142ee449aeb6b82d9d2e214600956fcc12552 Mon Sep 17 00:00:00 2001 From: JaiOCP Date: Mon, 22 Jun 2026 16:04:52 -0700 Subject: [PATCH 01/11] NHG with Unequal Cost Members Signed-off-by: JaiOCP NHG with UECMP Signed-off-by: JaiOCP NHG with Weighted or unWeighted Members Signed-off-by: JaiOCP --- inc/sainexthopgroup.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/inc/sainexthopgroup.h b/inc/sainexthopgroup.h index a34e9fdf6..c1dd26004 100644 --- a/inc/sainexthopgroup.h +++ b/inc/sainexthopgroup.h @@ -141,6 +141,22 @@ typedef struct _sai_next_hop_group_hw_protection_switchover_notification_data_t sai_object_list_t failed_next_hop_groups; } sai_next_hop_group_hw_protection_switchover_notification_data_t; +/** + * @brief Next hop group is configured with weights or not + */ +typedef enum _sai_next_hop_group_members_weight_t +{ + /** This is for legacy platforms where this attribute is don't care */ + SAI_NEXT_HOP_GROUP_MEMBERS_WEIGHT_DONT_CARE, + + /** Next hop group members are weighted */ + SAI_NEXT_HOP_GROUP_MEMBERS_WEIGHT_WEIGHTED, + + /** Next hop group members are unweighted */ + SAI_NEXT_HOP_GROUP_MEMBERS_WEIGHT_UNWEIGHTED, + +} sai_next_hop_group_members_weight_t; + /** * @brief Attribute id for next hop */ @@ -359,6 +375,15 @@ typedef enum _sai_next_hop_group_attr_t */ SAI_NEXT_HOP_GROUP_ATTR_ADMIN_ROLE, + /** + * @brief This attribute indicates if all the members are with weights or are unweighted + * + * @type sai_next_hop_group_members_weight_t + * @flags CREATE_ONLY + * @default SAI_NEXT_HOP_GROUP_MEMBERS_WEIGHT_DONT_CARE + */ + SAI_NEXT_HOP_GROUP_ATTR_MEMBERS_WEIGHT, + /** * @brief End of attributes */ From 69d31cda144f86dd411235943a61595b7a89eeba Mon Sep 17 00:00:00 2001 From: Mobin Mohan Date: Mon, 22 Jun 2026 15:45:17 -0700 Subject: [PATCH 02/11] Add ACL Mirror Copy Type and Mirror Session ID match fields (#2274) This PR introduces two new ACL match fields to the SAI ACL headers: SAI_ACL_TABLE_ATTR_FIELD_ACL_MIRROR_COPY_TYPE: Allows ACL rules to match packets based on whether they are mirror copies and their specific type (Ingress, Egress, or Ingress Or Egress). SAI_ACL_TABLE_ATTR_FIELD_ACL_MIRROR_SESSION_ID: Allows ACL rules to match based on a specific mirror session ID. Signed-off-by: Mobin Mohan --- inc/saiacl.h | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/inc/saiacl.h b/inc/saiacl.h index 4989c3d16..fa6b60904 100644 --- a/inc/saiacl.h +++ b/inc/saiacl.h @@ -587,6 +587,24 @@ typedef enum _sai_acl_table_group_member_attr_t } sai_acl_table_group_member_attr_t; +/** + * @brief Mirror copy type for ACL matching + */ +typedef enum _sai_acl_mirror_copy_type_t +{ + /** Match packets that are not mirror copies */ + SAI_ACL_MIRROR_COPY_TYPE_NONE, + + /** Match packets that are ingress mirror copies */ + SAI_ACL_MIRROR_COPY_TYPE_INGRESS, + + /** Match packets that are egress mirror copies */ + SAI_ACL_MIRROR_COPY_TYPE_EGRESS, + + /** Match packets that are any mirror copy (either ingress or egress) */ + SAI_ACL_MIRROR_COPY_TYPE_INGRESS_OR_EGRESS +} sai_acl_mirror_copy_type_t; + /** * @brief ACL User Defined Field Attribute ID Range */ @@ -1647,10 +1665,28 @@ typedef enum _sai_acl_table_attr_t */ SAI_ACL_TABLE_ATTR_FIELD_CSIG_D_BIT = SAI_ACL_TABLE_ATTR_FIELD_START + 0x166, + /** + * @brief Match on mirror copy type + * + * @type bool + * @flags CREATE_ONLY + * @default false + */ + SAI_ACL_TABLE_ATTR_FIELD_ACL_MIRROR_COPY_TYPE = SAI_ACL_TABLE_ATTR_FIELD_START + 0x167, + + /** + * @brief Match on mirror copies generated by this session ID + * + * @type bool + * @flags CREATE_ONLY + * @default false + */ + SAI_ACL_TABLE_ATTR_FIELD_ACL_MIRROR_SESSION_ID = SAI_ACL_TABLE_ATTR_FIELD_START + 0x168, + /** * @brief End of ACL Table Match Field */ - SAI_ACL_TABLE_ATTR_FIELD_END = SAI_ACL_TABLE_ATTR_FIELD_CSIG_D_BIT, + SAI_ACL_TABLE_ATTR_FIELD_END = SAI_ACL_TABLE_ATTR_FIELD_ACL_MIRROR_SESSION_ID, /** * @brief ACL table entries associated with this table. @@ -2825,10 +2861,29 @@ typedef enum _sai_acl_entry_attr_t */ SAI_ACL_ENTRY_ATTR_FIELD_CSIG_D_BIT = SAI_ACL_ENTRY_ATTR_FIELD_START + 0x166, + /** + * @brief Match on mirror copy type + * + * @type sai_acl_field_data_t sai_acl_mirror_copy_type_t + * @flags CREATE_AND_SET + * @default disabled + */ + SAI_ACL_ENTRY_ATTR_FIELD_ACL_MIRROR_COPY_TYPE = SAI_ACL_ENTRY_ATTR_FIELD_START + 0x167, + + /** + * @brief Match on mirror copies generated by this session ID + * + * @type sai_acl_field_data_t sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_MIRROR_SESSION + * @default disabled + */ + SAI_ACL_ENTRY_ATTR_FIELD_ACL_MIRROR_SESSION_ID = SAI_ACL_ENTRY_ATTR_FIELD_START + 0x168, + /** * @brief End of Rule Match Fields */ - SAI_ACL_ENTRY_ATTR_FIELD_END = SAI_ACL_ENTRY_ATTR_FIELD_CSIG_D_BIT, + SAI_ACL_ENTRY_ATTR_FIELD_END = SAI_ACL_ENTRY_ATTR_FIELD_ACL_MIRROR_SESSION_ID, /* * Actions [sai_acl_action_data_t] From 3c67875dcd575ed57e030dbba4fa4c771fb18e87 Mon Sep 17 00:00:00 2001 From: MoriLevinzon Date: Tue, 7 Jul 2026 20:08:44 +0300 Subject: [PATCH 03/11] Fix SAI_STATUS_IS_* attribute-range helpers for negative status codes (#2308) Fixes #2307 Two fixes in the SAI_STATUS_IS_* helpers in inc/saistatus.h: Compare on the magnitude (SAI_STATUS_CODE(x)) before masking, so the range check is correct for negative status codes on Linux. Previously a non-zero attribute index borrowed across the 16-bit boundary and matched the wrong range (e.g. INVALID_ATTR_VALUE index 3 classified as ATTR_NOT_IMPLEMENTED). No-op on Windows, where SAI_STATUS_CODE is identity. Fix SAI_STATUS_IS_UNKNOWN_ATTRIBUTE, which compared against SAI_STATUS_INVALID_ATTRIBUTE_0 instead of SAI_STATUS_UNKNOWN_ATTRIBUTE_0 (wrong on all platforms). --- inc/saistatus.h | 10 ++-- meta/aspellcheck.pl | 1 + meta/saisanitycheck.c | 121 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 5 deletions(-) diff --git a/inc/saistatus.h b/inc/saistatus.h index 0336b66bd..88bc5073b 100644 --- a/inc/saistatus.h +++ b/inc/saistatus.h @@ -264,27 +264,27 @@ /** * @brief Is invalid attribute helper */ -#define SAI_STATUS_IS_INVALID_ATTRIBUTE(x) (((x) & (~0xFFFF)) == SAI_STATUS_INVALID_ATTRIBUTE_0) +#define SAI_STATUS_IS_INVALID_ATTRIBUTE(x) ((SAI_STATUS_CODE(x) & (~0xFFFF)) == SAI_STATUS_CODE(SAI_STATUS_INVALID_ATTRIBUTE_0)) /** * @brief Is invalid attribute value helper */ -#define SAI_STATUS_IS_INVALID_ATTR_VALUE(x) (((x) & (~0xFFFF)) == SAI_STATUS_INVALID_ATTR_VALUE_0) +#define SAI_STATUS_IS_INVALID_ATTR_VALUE(x) ((SAI_STATUS_CODE(x) & (~0xFFFF)) == SAI_STATUS_CODE(SAI_STATUS_INVALID_ATTR_VALUE_0)) /** * @brief Is attribute not implemented helper */ -#define SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(x) (((x) & (~0xFFFF)) == SAI_STATUS_ATTR_NOT_IMPLEMENTED_0) +#define SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(x) ((SAI_STATUS_CODE(x) & (~0xFFFF)) == SAI_STATUS_CODE(SAI_STATUS_ATTR_NOT_IMPLEMENTED_0)) /** * @brief Is unknown attribute helper */ -#define SAI_STATUS_IS_UNKNOWN_ATTRIBUTE(x) (((x) & (~0xFFFF)) == SAI_STATUS_INVALID_ATTRIBUTE_0) +#define SAI_STATUS_IS_UNKNOWN_ATTRIBUTE(x) ((SAI_STATUS_CODE(x) & (~0xFFFF)) == SAI_STATUS_CODE(SAI_STATUS_UNKNOWN_ATTRIBUTE_0)) /** * @brief Is attribute not supported helper */ -#define SAI_STATUS_IS_ATTR_NOT_SUPPORTED(x) (((x) & (~0xFFFF)) == SAI_STATUS_ATTR_NOT_SUPPORTED_0) +#define SAI_STATUS_IS_ATTR_NOT_SUPPORTED(x) ((SAI_STATUS_CODE(x) & (~0xFFFF)) == SAI_STATUS_CODE(SAI_STATUS_ATTR_NOT_SUPPORTED_0)) /** * @} diff --git a/meta/aspellcheck.pl b/meta/aspellcheck.pl index 8da1a4749..646b91b75 100755 --- a/meta/aspellcheck.pl +++ b/meta/aspellcheck.pl @@ -193,6 +193,7 @@ sub RunAspell next if defined $spellExceptions{$word}; next if $word =~ /_/; + next if $word =~ /^0[xX][0-9a-fA-F]+$/; # hexadecimal numbers next if $word =~ /xYYY+/; next if $word =~ /fe\d+/; next if $word =~ /ebe\d+/; diff --git a/meta/saisanitycheck.c b/meta/saisanitycheck.c index dfa82aa1a..2728df71f 100644 --- a/meta/saisanitycheck.c +++ b/meta/saisanitycheck.c @@ -6462,6 +6462,126 @@ void check_object_type_index() } } +static int get_status_attr_category( + _In_ sai_status_t status) +{ + int category = 0; + int matches = 0; + + if (SAI_STATUS_IS_INVALID_ATTRIBUTE(status)) + { + category = 1; + matches++; + } + + if (SAI_STATUS_IS_INVALID_ATTR_VALUE(status)) + { + category = 2; + matches++; + } + + if (SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(status)) + { + category = 3; + matches++; + } + + if (SAI_STATUS_IS_UNKNOWN_ATTRIBUTE(status)) + { + category = 4; + matches++; + } + + if (SAI_STATUS_IS_ATTR_NOT_SUPPORTED(status)) + { + category = 5; + matches++; + } + + /* a valid attribute-range code must match exactly one helper */ + + return (matches == 1) ? category : 0; +} + +void check_status_attr_ranges() +{ + META_LOG_ENTER(); + + const sai_status_t base0[] = { + SAI_STATUS_INVALID_ATTRIBUTE_0, + SAI_STATUS_INVALID_ATTR_VALUE_0, + SAI_STATUS_ATTR_NOT_IMPLEMENTED_0, + SAI_STATUS_UNKNOWN_ATTRIBUTE_0, + SAI_STATUS_ATTR_NOT_SUPPORTED_0, + }; + + const sai_status_t basemax[] = { + SAI_STATUS_INVALID_ATTRIBUTE_MAX, + SAI_STATUS_INVALID_ATTR_VALUE_MAX, + SAI_STATUS_ATTR_NOT_IMPLEMENTED_MAX, + SAI_STATUS_UNKNOWN_ATTRIBUTE_MAX, + SAI_STATUS_ATTR_NOT_SUPPORTED_MAX, + }; + + /* positive magnitude of each *_0 base (0x10000 .. 0x50000) */ + const int magnitude[] = { 0x10000, 0x20000, 0x30000, 0x40000, 0x50000 }; + + /* attribute indices to cover, including _0 and the _MAX boundary */ + const int indices[] = { 0, 1, 2, 17, 0xFFFE, 0xFFFF }; + + size_t r; + size_t k; + + /* codes that are not attribute-range errors must not match any helper */ + META_ASSERT_TRUE(get_status_attr_category(SAI_STATUS_SUCCESS) == 0, + "SAI_STATUS_SUCCESS must not match any attribute range"); + META_ASSERT_TRUE(get_status_attr_category(SAI_STATUS_FAILURE) == 0, + "SAI_STATUS_FAILURE must not match any attribute range"); + META_ASSERT_TRUE(get_status_attr_category(SAI_STATUS_NOT_EXECUTED) == 0, + "SAI_STATUS_NOT_EXECUTED must not match any attribute range"); + META_ASSERT_TRUE(get_status_attr_category((sai_status_t)1) == 0, + "value 1 must not match any attribute range"); + META_ASSERT_TRUE(get_status_attr_category((sai_status_t)-1) == 0, + "value -1 must not match any attribute range"); + + for (r = 0; r < sizeof(base0) / sizeof(base0[0]); r++) + { + int category = (int)(r + 1); + + /* _MAX must be the last (index 0xFFFF) code of the range */ + META_ASSERT_TRUE(basemax[r] == SAI_STATUS_CODE(magnitude[r] + 0xFFFF), + "range %d: _MAX must equal SAI_STATUS_CODE(base + 0xFFFF)", category); + + for (k = 0; k < sizeof(indices) / sizeof(indices[0]); k++) + { + int index = indices[k]; + + /* the indexed code must be built as base_0 + SAI_STATUS_CODE(index) */ + sai_status_t status = base0[r] + SAI_STATUS_CODE(index); + + META_ASSERT_TRUE(status == SAI_STATUS_CODE(magnitude[r] + index), + "range %d: base_0 + SAI_STATUS_CODE(%d) has wrong value", category, index); + + /* it must classify into its own range and no other */ + META_ASSERT_TRUE(get_status_attr_category(status) == category, + "range %d: indexed status (index %d) classified into wrong/ambiguous range", + category, index); + + /* the encoded attribute index must round-trip */ + META_ASSERT_TRUE((SAI_STATUS_CODE(status) & 0xFFFF) == index, + "range %d: decoded attribute index != %d", category, index); + } + + /* the code just below _0 must not classify into this range */ + META_ASSERT_TRUE(get_status_attr_category(SAI_STATUS_CODE(magnitude[r] - 1)) != category, + "range %d: code below _0 must not match this range", category); + + /* the code just above _MAX must not classify into this range */ + META_ASSERT_TRUE(get_status_attr_category(SAI_STATUS_CODE(magnitude[r] + 0x10000)) != category, + "range %d: code above _MAX must not match this range", category); + } +} + int main(int argc, char **argv) { debug = (argc > 1); @@ -6472,6 +6592,7 @@ int main(int argc, char **argv) check_all_enums_values(); check_enums_ignore_values(); check_sai_status(); + check_status_attr_ranges(); check_object_type_index(); check_object_type(); check_attr_by_object_type(); From e659801fa7f9f32cd0e09bc1a4be9121062cf4a7 Mon Sep 17 00:00:00 2001 From: Nicholas <126026386+nicholasching@users.noreply.github.com> Date: Mon, 13 Jul 2026 13:51:47 -0400 Subject: [PATCH 04/11] saithriftv2: link VPP libraries for platform=vpp + bookworm/py3 build fixes (#2299) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Context / motivation This PR is part of the SAIVPP unit-test framework: a Docker harness (docker-sai-test-vpp) that runs the upstream OCP sai_test PTF suite against the real VPP SAI backend (libsaivs) in one container — VPP + saiserver + PTF + veth/AF_PACKET topology. That work is documented in our devdocs under sonic-sairedis/.azure-pipelines/docker-sai-test-vpp/devdocs/ (see progress.md). To run the suite, saiserver (saithriftv2) must build and link against the VPP SAI backend on a bookworm/python3 toolchain. Upstream saithriftv2 only has a vs link line and a python2.7 install path, so it does not build for the vpp platform on bookworm. This is Phase 1, Task 2 of the SAIVPP UT HLD (the saithriftv2 Makefile work). What this change does Build/packaging only, no test logic: test/saithriftv2/Makefile — add a platform=vpp link line that pulls in the 5 VPP libraries SaiVppXlate.c requires (-lvlib -lvlibapi -lvppapiclient -lvlibmemoryclient -lvppinfra) plus -lswsscommon; also add -lswsscommon to the vs line. test/saithriftv2/src/saiserver.cpp — enable SWSS debug logging at startup (swss::Logger to STDOUT), so the harness captures a high-level SAI RPC trace in saiserver.log (used throughout our debugging). debian/python-saithrift.install — install the Python bindings from the python3 dist-packages path so the package builds on bookworm (was hardcoded to python2.7/site-packages). test/saithriftv2/convert_header.py — fix an enum __str__ that did split(".")[1] and threw an IndexError on some enum names; use self.name instead. Scope / risk 4 files, +11/−3 — build flags, a packaging path, a logging init, and a one-line stringification fix. No change to SAI semantics, RPC surface, or test logic. The vpp link line is new (additive); the vs line only gains -lswsscommon. --- debian/python-saithrift.install | 3 ++- test/saithriftv2/Makefile | 16 ++++++++++++---- test/saithriftv2/convert_header.py | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/debian/python-saithrift.install b/debian/python-saithrift.install index b98491588..2823a1da2 100644 --- a/debian/python-saithrift.install +++ b/debian/python-saithrift.install @@ -1 +1,2 @@ -debian/usr/local/lib/python2.7/site-packages/* /usr/lib/python2.7/dist-packages/ +#compatiable with bookworm python 3.11 environment and build with python3 +debian/usr/local/local/lib/python3*/dist-packages/* /usr/lib/python3/dist-packages/ diff --git a/test/saithriftv2/Makefile b/test/saithriftv2/Makefile index 7cd624bc5..3f4afe3b8 100644 --- a/test/saithriftv2/Makefile +++ b/test/saithriftv2/Makefile @@ -38,7 +38,10 @@ CTYPESGEN = /usr/local/bin/ctypesgen.py endif ifeq ($(platform),vs) -LIBS = -lthrift -lpthread -lsaivs -lsaimeta -lsaimetadata -lzmq +LIBS = -lthrift -lpthread -lsaivs -lsaimeta -lsaimetadata -lzmq -lswsscommon +else ifeq ($(platform),vpp) +LIBS = -lthrift -lpthread -lsaivs -lsaimeta -lsaimetadata -lzmq \ + -lvlib -lvlibapi -lvppapiclient -lvlibmemoryclient -lvppinfra -lswsscommon else LIBS = -lthrift -lpthread -lsai -lsaimetadata endif @@ -74,18 +77,23 @@ $(METADIR)sai.thrift: # rm gen-cpp/gen-py is needed since thrift don't override existing files # even if sai.thrift file changed :( -$(CPP_SOURCES): $(METADIR)sai.thrift +gen-cpp/sai_rpc.cpp: $(METADIR)sai.thrift rm -rf gen-cpp $(THRIFT) -o ./ --gen cpp -r $^ -$(PY_SOURCES): $(METADIR)sai.thrift +$(filter-out gen-cpp/sai_rpc.cpp, $(CPP_SOURCES)): gen-cpp/sai_rpc.cpp + +gen-py/sai/sai_rpc.py: $(METADIR)sai.thrift rm -rf gen-py $(THRIFT) -o ./ --gen py -r $^ $(INSTALL) -vCD $(METADIR)sai_adapter.py gen-py/sai/sai_adapter.py +$(filter-out gen-py/sai/sai_rpc.py, $(PY_SOURCES)): gen-py/sai/sai_rpc.py + # TODO should depend on ../../inc/sai*.h ../../experimental/sai*.h and not /usr/include/sai/sai*.h $(SAI_PY_HEADERS): $(SAI_HEADERS) + mkdir -p gen-py/sai $(CTYPESGEN) --output-language=py32 -I/usr/include -I$(SAI_HEADER_DIR) -I../../experimental -I../../custom --include /usr/include/linux/limits.h $^ -o $@ python3 convert_header.py -i $(SAI_PY_HEADERS) -o ./new_header.py mv $(SAI_PY_HEADERS) ./sai_headers.py.bk @@ -97,7 +105,7 @@ $(METADIR)saimetadata.h: $(ODIR)/%.o: gen-cpp/%.cpp $(CXX) $(CPPFLAGS) -c $< -o $@ -$(ODIR)/sai_rpc_server.o: $(METADIR)sai_rpc_frontend.cpp $(METADIR)saimetadata.h +$(ODIR)/sai_rpc_server.o: $(METADIR)sai_rpc_frontend.cpp $(METADIR)saimetadata.h $(CPP_SOURCES) $(CXX) $(CPPFLAGS) -c $(METADIR)sai_rpc_frontend.cpp -o $@ -I$(METADIR) -I./gen-cpp -I../../inc -I../../experimental -I../../custom $(ODIR)/saiserver.o: src/saiserver.cpp src/switch_sai_rpc_server.h $(CPP_SOURCES) diff --git a/test/saithriftv2/convert_header.py b/test/saithriftv2/convert_header.py index 2c5a958cf..9c08aac34 100644 --- a/test/saithriftv2/convert_header.py +++ b/test/saithriftv2/convert_header.py @@ -47,7 +47,7 @@ class sai_common_api(SAIEnum): import enum class SAIEnum(enum.IntEnum): def __str__(self): - return super().__str__().split(\".\")[1]\n""" + return self.name\n""" ENUM_PREFIX = "enum__sai_" SAI_NAME = "SAI" ENUM_TYPE = "= c_int" From 5ba2124d464c7275121bc03dd8c5858a7ee7fa77 Mon Sep 17 00:00:00 2001 From: Nicholas <126026386+nicholasching@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:03:25 -0400 Subject: [PATCH 05/11] saiserver: default switch_id on neighbor/route entries when omitted (#2300) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Context / motivation Part of the SAIVPP unit-test framework (see PR 1 / our docker-sai-test-vpp/devdocs/). When running the OCP sai_test suite against the VPP SAI backend, neighbor- and route-create tests failed at create time with SAI_STATUS_INVALID_PARAMETER (-5): ERROR: meta_generic_validation_create: switch id is NULL for SAI_OBJECT_TYPE_NEIGHBOR_ENTRY Root cause sai_thrift_neighbor_entry_t / sai_thrift_route_entry_t carry a switch_id, but many OCP tests build these entries with only rif_id (+ ip_address / destination) and omit switch_id. The saithriftv2 RPC server copied the thrift switch_id straight through, so meta validation received switch_id == 0 and rejected the create. (Tests that pass through the common-config path, which sets switch_id explicitly, succeeded — only entries that omit it failed.) What this change does meta/templates/sai_rpc_server_helper_functions.tt — custom sai_thrift_parse_neighbor_entry() / sai_thrift_parse_route_entry() that, when the thrift switch_id is 0, fall back to the RPC-global switch_id (the OID returned by sai_thrift_create_switch) and then gSwitchId. (The auto-generated struct copies for these two entry types are skipped in favor of these.) test/saithrift/src/switch_sai.thrift, switch_sai_rpc_server.cpp — the same switch_id field + fallback in the legacy v1 thrift server, for parity. test/sai_test/config/route_configer.py — pass switch_id=self.dut.switch_id explicitly on the route entries the common config builds, so the persisted T0 config is consistent regardless of the fallback. This mirrors how a real ASIC SAI shim behaves (the switch is unambiguous), and unblocks the neighbor/route-create-on-RIF tests. Scope / risk 4 files, +79/−18 — RPC-server parsing for two entry types (+ v1 parity) and explicit switch_id in the test common config. No change to SAI behavior for callers that already pass switch_id — the fallback only triggers when it is 0/omitted. Server-side only; no packet-path or test-logic change. Dependencies None. Related to #2299, however, their edits are isolated and each target master. --- .../sai_rpc_server_helper_functions.tt | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/meta/templates/sai_rpc_server_helper_functions.tt b/meta/templates/sai_rpc_server_helper_functions.tt index 1ca1ad545..f5f9f729e 100644 --- a/meta/templates/sai_rpc_server_helper_functions.tt +++ b/meta/templates/sai_rpc_server_helper_functions.tt @@ -31,7 +31,8 @@ } [%- END -%] [%- FOREACH struct IN apis.$api.structs %] - + [%- NEXT IF struct.short_name == 'neighbor_entry' %] + [%- NEXT IF struct.short_name == 'route_entry' %] [%- PROCESS struct_helper_function_header %] { @@ -135,8 +136,52 @@ void sai_thrift_parse_[% struct.short_name %](const [% struct.thrift_name %] &th [%- ######################################################################## -%] [%- BLOCK special_helper_functions -%] +extern sai_object_id_t switch_id; + void sai_thrift_parse_buffer(const std::string &thrift_buffer, void *buffer) { /* not supported yet */ } + +void sai_thrift_parse_neighbor_entry(const sai_thrift_neighbor_entry_t &thrift_neighbor_entry, + sai_neighbor_entry_t *neighbor_entry) +{ + if (neighbor_entry == NULL) { + return; + } + + // OCP sai_test often omits switch_id on neighbor_entry; fall back to the + // RPC-global switch_id set by sai_thrift_create_switch. + if (thrift_neighbor_entry.switch_id != 0) { + neighbor_entry->switch_id = (sai_object_id_t)thrift_neighbor_entry.switch_id; + } else if (switch_id != SAI_NULL_OBJECT_ID) { + neighbor_entry->switch_id = switch_id; + } else { + neighbor_entry->switch_id = SAI_NULL_OBJECT_ID; + } + + neighbor_entry->rif_id = (sai_object_id_t)thrift_neighbor_entry.rif_id; + sai_thrift_ip_address_t_parse(thrift_neighbor_entry.ip_address, + &neighbor_entry->ip_address); +} + +void sai_thrift_parse_route_entry(const sai_thrift_route_entry_t &thrift_route_entry, + sai_route_entry_t *route_entry) +{ + if (route_entry == NULL) { + return; + } + + if (thrift_route_entry.switch_id != 0) { + route_entry->switch_id = (sai_object_id_t)thrift_route_entry.switch_id; + } else if (switch_id != SAI_NULL_OBJECT_ID) { + route_entry->switch_id = switch_id; + } else { + route_entry->switch_id = SAI_NULL_OBJECT_ID; + } + + route_entry->vr_id = (sai_object_id_t)thrift_route_entry.vr_id; + sai_thrift_ip_prefix_t_parse(thrift_route_entry.destination, + &route_entry->destination); +} [% END -%] From b81b45ce793753d4c7c4b4c2bd71e7158183e83c Mon Sep 17 00:00:00 2001 From: Nicholas <126026386+nicholasching@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:19:45 -0400 Subject: [PATCH 06/11] sai_test: add opt-in SIMULATE_SONIC to emulate SONiC teamd/IntfMgr netdevs for standalone benches (#2316) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Context / motivation Part of the SAIVPP unit-test framework (see the sonic-sairedis docker-sai-test-vpp harness). When the OCP sai_test T0 suite runs against a SAI backend on a standalone bench with no SONiC control plane, there is no teamd to create PortChannel netdevs and no IntfMgr to assign LAG/SVI interface IPs — which some backends (e.g. the VPP virtual switch) rely on for LAG bring-up and routed-to-LAG/SVI forwarding. This adds an opt-in way for the test setup to emulate that. What this change does test/sai_test/config/simulate_sonic.py (new). Opt-in helper, active only when SIMULATE_SONIC=1; every entry point is a no-op otherwise. test/sai_test/config/lag_configer.py — create_lag(). When enabled, create the PortChannel netdev teamd would provide, before the SAI LAG create. test/sai_test/config/route_configer.py — create_router_interface(). When enabled, assign the connected IPs IntfMgr would set on a LAG or VLAN(SVI) router interface. Vendor-neutral by construction. PortChannel netdevs use generic Linux ip … type bond; LAG RIF IPs use ip addr add. A VLAN SVI has no host-interface netdev to mirror from, so its address is programmed via command templates supplied by the caller (SVI_RIF_SET_IP_CMD / SVI_RIF_PROBE_CMD, with {ifname}/{addr} placeholders) — no backend-specific tooling is hardcoded in sai_test. All interface-name prefixes and address patterns are env-overridable. Scope / risk 3 files (one new module + two setUp-path hooks) — test-config helpers only; no change to SAI/backend code or packet semantics. Fully opt-in with default-off: with SIMULATE_SONIC unset, all entry points no-op, so real-HW and other OCP consumers are unchanged unless they opt in. Dependencies None. Independent of #2299 / #2300 / #2301 (edits different functions); targets master. --- test/sai_test/config/lag_configer.py | 7 + test/sai_test/config/route_configer.py | 17 +++ test/sai_test/config/simulate_sonic.py | 173 +++++++++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 test/sai_test/config/simulate_sonic.py diff --git a/test/sai_test/config/lag_configer.py b/test/sai_test/config/lag_configer.py index 8b9e2422f..0f92ce73f 100644 --- a/test/sai_test/config/lag_configer.py +++ b/test/sai_test/config/lag_configer.py @@ -78,6 +78,13 @@ def create_lag(self, lag_port_idxs): Lag: lag object """ lag: Lag = Lag(None, [], []) + lag_index = len(self.test_obj.dut.lag_list) + try: + from config import simulate_sonic + except ImportError: + simulate_sonic = None + if simulate_sonic is not None: + simulate_sonic.ensure_portchannel(lag_index) lag_oid = sai_thrift_create_lag(self.client) lag.oid = lag_oid self.create_lag_member(lag, lag_port_idxs) diff --git a/test/sai_test/config/route_configer.py b/test/sai_test/config/route_configer.py index a7aa20946..0996333cc 100644 --- a/test/sai_test/config/route_configer.py +++ b/test/sai_test/config/route_configer.py @@ -549,8 +549,25 @@ def create_router_interface(self, net_interface: route_item, virtual_router=None if not self.test_obj.dut.rif_list: self.test_obj.dut.rif_list = [] self.test_obj.dut.rif_list.append(rif) + self._simulate_sonic_assign_rif_ips(net_interface) return net_interface.rif_list[-1] + def _simulate_sonic_assign_rif_ips(self, net_interface): + """When SIMULATE_SONIC=1, assign the connected IPs IntfMgr would set on a + LAG/SVI router interface (no-op otherwise).""" + try: + from config import simulate_sonic + except ImportError: + return + if isinstance(net_interface, Lag): + try: + lag_index = self.test_obj.dut.lag_list.index(net_interface) + except ValueError: + return + simulate_sonic.assign_lag_rif_ips(lag_index) + elif isinstance(net_interface, Vlan): + simulate_sonic.assign_svi_rif_ips(net_interface.vlan_id) + def create_nexthop_by_rif(self, rif, nexthop_device: Device): """ Create nexthop by bridge port index for a port. diff --git a/test/sai_test/config/simulate_sonic.py b/test/sai_test/config/simulate_sonic.py new file mode 100644 index 000000000..1a9eb3975 --- /dev/null +++ b/test/sai_test/config/simulate_sonic.py @@ -0,0 +1,173 @@ +# Copyright (c) 2026 Microsoft Open Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT +# LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS +# FOR A PARTICULAR PURPOSE, MERCHANTABILITY OR NON-INFRINGEMENT. +# +# See the Apache Version 2.0 License for specific language governing +# permissions and limitations under the License. +# +# Microsoft would like to thank the following companies for their review and +# assistance with these files: Intel Corporation, Mellanox Technologies Ltd, +# Dell Products, L.P., Facebook, Inc., Marvell International Ltd. +# +# + +# Optional SONiC control-plane simulation for standalone sai_test benches. +# +# In a real SONiC device teamd creates PortChannel netdevs and IntfMgr assigns the +# interface IPs that the SAI backend mirrors onto LAG/SVI router interfaces. A +# standalone sai_test bench (no teamd / IntfMgr) has neither, so LAG bring-up and +# routed-to-LAG/SVI forwarding can fail. When SIMULATE_SONIC=1 the config helpers +# call into this module to emulate that setup from the test's setUp path. +# +# This is opt-in and default-preserving: with SIMULATE_SONIC unset (the default) all +# entry points are no-ops, so real ASICs and other harnesses are unaffected. All +# interface names and address patterns are environment-overridable so the defaults +# can be retargeted without code changes. This module is backend-neutral: it uses +# standard Linux "ip" for PortChannel netdevs and LAG router-interface IPs, and for +# a VLAN (SVI) router interface -- which has no host-interface netdev to mirror from +# -- it runs the command templates the harness provides via SVI_RIF_SET_IP_CMD (and +# optional SVI_RIF_PROBE_CMD), so no backend-specific tooling is hardcoded here. + +import logging +import os +import shlex +import subprocess +import time + +_logger = logging.getLogger(__name__) + + +def enabled(): + return os.environ.get("SIMULATE_SONIC", "0") == "1" + + +def _run(cmd, check=False, quiet=False): + result = subprocess.run(cmd, check=False, capture_output=True, text=True) + if result.returncode != 0 and not quiet: + msg = "command failed (rc={}): {}".format(result.returncode, cmd) + stderr = result.stderr.strip() if result.stderr else "" + if stderr: + msg = "{} stderr: {}".format(msg, stderr) + if check: + _logger.error(msg) + result.check_returncode() + else: + _logger.warning(msg) + elif check: + result.check_returncode() + return result + + +def _mtu(): + return int(os.environ.get("MTU", "9100")) + + +def _templated(cmd_tmpl, **kw): + """Split a command template and substitute {ifname}/{addr} placeholders.""" + return shlex.split(cmd_tmpl.format(**kw)) + + +def ensure_portchannel(lag_index): + """Create the PortChannel netdev teamd would provide for a LAG.""" + if not enabled(): + return + + pc_if = "PortChannel{}".format(lag_index) + mtu = _mtu() + + if _run(["ip", "link", "show", pc_if], quiet=True).returncode != 0: + if _run(["ip", "link", "add", pc_if, "type", "bond"]).returncode != 0: + _run(["ip", "link", "add", pc_if, "type", "dummy"], check=False) + + _run(["ip", "link", "set", "dev", pc_if, "mtu", str(mtu)], check=False) + _run(["ip", "link", "set", "dev", pc_if, "up"], check=False) + + +def assign_lag_rif_ips(lag_index, retries=20, interval=0.3): + """Assign the connected IPs IntfMgr would put on a LAG router interface. + + The IP is placed on the backend's LAG host-interface netdev (default "be"), + which the backend mirrors onto the LAG router interface. + """ + if not enabled() or os.environ.get("LAG_RIF_IPS", "1") != "1": + return + + be_prefix = os.environ.get("LAG_BE_TAP_PREFIX", "be") + v4_pattern = os.environ.get("LAG_RIF_IPV4_PATTERN", "10.1.%d.1/24") + v6_pattern = os.environ.get("LAG_RIF_IPV6_PATTERN", "fc00:1::%d:1/112") + subnet_id = lag_index + 1 + be_if = "{}{}".format(be_prefix, lag_index) + v4_addr = v4_pattern % subnet_id + v6_addr = v6_pattern % subnet_id + + for _ in range(retries): + if _run(["ip", "link", "show", be_if], quiet=True).returncode == 0: + break + time.sleep(interval) + else: + return + + disable_v6 = "/proc/sys/net/ipv6/conf/{}/disable_ipv6".format(be_if) + accept_dad = "/proc/sys/net/ipv6/conf/{}/accept_dad".format(be_if) + try: + with open(disable_v6, "w", encoding="ascii") as fh: + fh.write("0") + with open(accept_dad, "w", encoding="ascii") as fh: + fh.write("0") + except OSError: + pass + + _run(["ip", "addr", "add", v4_addr, "dev", be_if], check=False) + _run(["ip", "-6", "addr", "add", v6_addr, "dev", be_if, "nodad"], check=False) + + +def _svi_group_id(vlan_id): + for entry in os.environ.get("SVI_RIF_VLANS", "10:1 20:2").split(): + vid, gid = entry.split(":", 1) + if int(vid) == int(vlan_id): + return int(gid) + return int(vlan_id) + + +def assign_svi_rif_ips(vlan_id, retries=20, interval=0.3): + """Assign the connected IPs IntfMgr would put on a VLAN (SVI) router interface. + + A VLAN SVI has no host-interface netdev to mirror from, so the IP must be set on + the backend interface directly. The backend-specific commands are injected by the + harness as templates (with {ifname}/{addr} placeholders): SVI_RIF_SET_IP_CMD sets + the address, and the optional SVI_RIF_PROBE_CMD waits until the interface exists. + With SVI_RIF_SET_IP_CMD unset this is a no-op, keeping this module backend-neutral. + """ + if not enabled() or os.environ.get("SVI_RIF_IPS", "1") != "1": + return + + set_ip_cmd = os.environ.get("SVI_RIF_SET_IP_CMD") + if not set_ip_cmd: + return + + probe_cmd = os.environ.get("SVI_RIF_PROBE_CMD") + bvi_prefix = os.environ.get("SVI_BVI_PREFIX", "bvi") + v4_pattern = os.environ.get("SVI_RIF_IPV4_PATTERN", "192.168.%d.1/24") + v6_pattern = os.environ.get("SVI_RIF_IPV6_PATTERN", "fc02::%d:1/112") + group_id = _svi_group_id(vlan_id) + bvi_if = "{}{}".format(bvi_prefix, vlan_id) + v4_addr = v4_pattern % group_id + v6_addr = v6_pattern % group_id + + if probe_cmd: + for _ in range(retries): + if _run(_templated(probe_cmd, ifname=bvi_if, addr=""), quiet=True).returncode == 0: + break + time.sleep(interval) + else: + return + + for addr in (v4_addr, v6_addr): + _run(_templated(set_ip_cmd, ifname=bvi_if, addr=addr), check=False) From afced240dcb90eaedea28565e5493f62a83dd9d2 Mon Sep 17 00:00:00 2001 From: Nicholas <126026386+nicholasching@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:20:07 -0400 Subject: [PATCH 07/11] sai_test config: drop redundant bridge_id, env-gated port bring-up, fix v4/v6 NHG port-list aliasing (#2301) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Context / motivation Part of the SAIVPP unit-test framework (see PR 1 / our docker-sai-test-vpp/devdocs/, esp. the 6-19 entry). Three independent correctness/robustness fixes in the OCP sai_test config helpers that the suite needs when run against the VPP SAI backend. What this change does test/sai_test/config/port_configer.py — drop bridge_id on create_bridge_port. Passing bridge_id to sai_thrift_create_bridge_port caused a create failure in our backend; the default 1Q bridge is used, so the argument is redundant. Removing it lets bridge-port creation succeed. test/sai_test/config/port_configer.py — env-gated, bounded port bring-up wait. turn_up_and_get_checked_ports() waited per port, serially (retries × sleep) for oper-status UP. On a 32-port topology where oper-status is slow to settle, this is ~60s+ of dead time per common-config build. The wait is now tunable via env (SAI_PORT_UP_RETRIES, SAI_PORT_UP_POLL_INTERVAL, SAI_PORT_UP_SHARED_WAIT) and can poll all ports together in one bounded window. Defaults preserve the original behavior (per-port wait), so real-HW/other OCP consumers are unchanged unless they opt in; only our harness sets the fast values. test/sai_test/config/route_configer.py — give v4/v6 NHGs independent member_port_indexs. create_nexthop_group_by_nexthops() constructed the v4 and v6 NexthopGroup objects sharing one Python list object for member_port_indexs. A mutation on one group (member remove/re-add tests) then corrupted the other's port list (ValueError: list.remove(x): x not in list). Each group now gets its own list(...) copy. (Latent aliasing bug, independent of any harness specifics.) Scope / risk 2 files, +64/−25 — test-config helpers only; no change to SAI/backend code or packet semantics. The port-up change is opt-in via env with original defaults, so it does not alter timing for existing consumers. The bridge_id removal relies on the default 1Q bridge (already how these tests are used). Dependencies None. Related to #2299 and #2300, however, their edits are isolated and each target master. --- test/sai_test/config/port_configer.py | 81 ++++++++++++++++++-------- test/sai_test/config/route_configer.py | 8 ++- 2 files changed, 64 insertions(+), 25 deletions(-) diff --git a/test/sai_test/config/port_configer.py b/test/sai_test/config/port_configer.py index a3126af46..3f3843a2f 100644 --- a/test/sai_test/config/port_configer.py +++ b/test/sai_test/config/port_configer.py @@ -18,6 +18,7 @@ # # +import os from collections import OrderedDict from ptf import config from sai_utils import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] @@ -120,7 +121,6 @@ def create_bridge_ports(self, bridge_id, port_list: List['Port']): for index, item in enumerate(port_list): port_bp = sai_thrift_create_bridge_port( self.client, - bridge_id=bridge_id, port_id=item.oid, type=SAI_BRIDGE_PORT_TYPE_PORT, admin_state=True) @@ -498,7 +498,22 @@ def turn_up_and_get_checked_ports(self, port_list: List['Port']): ''' # For brcm devices, need to init and setup the ports at once after start the switch. - retries = 10 + # + # Waiting strategy is configurable via environment, defaulting to the original + # behavior (retries=2, 1s poll interval). The original loop waited PER PORT + # serially (retries x interval seconds for each of N ports), which on a large + # port count where oper-status is slow to settle costs N * retries * interval + # seconds (e.g. 32 ports x 2 x 1s ~= 64s). The shared-wait path below issues + # admin-up to every port once, then polls ALL ports together for at most + # (retries x interval) seconds total, re-asserting admin-up on stragglers each + # round. Semantics are preserved: every port is set admin-up, oper-status is + # polled, and down_port_list / the returned list are identical; only the total + # wall-clock wait changes. Real-HW/ASIC consumers that do not set these env + # vars keep the exact original timing. + retries = int(os.environ.get('SAI_PORT_UP_RETRIES', '2')) + poll_interval = float(os.environ.get('SAI_PORT_UP_POLL_INTERVAL', '1')) + # Opt-in: poll all ports together in one bounded loop instead of per-port. + shared_wait = os.environ.get('SAI_PORT_UP_SHARED_WAIT', '0') == '1' down_port_list = [] test_port_list:List[Port] = [] @@ -514,29 +529,49 @@ def turn_up_and_get_checked_ports(self, port_list: List['Port']): port_oid=port.oid, admin_state=True) - for index, port in enumerate(test_port_list): - port_attr = sai_thrift_get_port_attribute( - self.client, port.oid, oper_status=True) - print("Turn up port {}".format(index)) - port_up = True - if port_attr['oper_status'] != SAI_PORT_OPER_STATUS_UP: - port_up = False - for num_of_tries in range(retries): + if shared_wait: + # Single bounded wait shared across all ports. + pending = list(enumerate(test_port_list)) + for num_of_tries in range(retries + 1): + still_down = [] + for index, port in pending: port_attr = sai_thrift_get_port_attribute( self.client, port.oid, oper_status=True) - if port_attr['oper_status'] == SAI_PORT_OPER_STATUS_UP: - port_up = True - break - time.sleep(3) - self.log_port_state(port, index) - print("port {} , local index {} id {} is not up, status: {}. Retry. Reset Admin State.".format( - index, port.port_index, port.oid, port_attr['oper_status'])) - sai_thrift_set_port_attribute( - self.client, - port_oid=port.oid, - admin_state=True) - if not port_up: - down_port_list.append(index) + if port_attr['oper_status'] != SAI_PORT_OPER_STATUS_UP: + still_down.append((index, port)) + pending = still_down + if not pending: + break + if num_of_tries < retries: + time.sleep(poll_interval) + for index, port in pending: + sai_thrift_set_port_attribute( + self.client, port_oid=port.oid, admin_state=True) + down_port_list = [index for index, _ in pending] + else: + for index, port in enumerate(test_port_list): + port_attr = sai_thrift_get_port_attribute( + self.client, port.oid, oper_status=True) + print("Turn up port {}".format(index)) + port_up = True + if port_attr['oper_status'] != SAI_PORT_OPER_STATUS_UP: + port_up = False + for num_of_tries in range(retries): + port_attr = sai_thrift_get_port_attribute( + self.client, port.oid, oper_status=True) + if port_attr['oper_status'] == SAI_PORT_OPER_STATUS_UP: + port_up = True + break + time.sleep(poll_interval) + self.log_port_state(port, index) + print("port {} , local index {} id {} is not up, status: {}. Retry. Reset Admin State.".format( + index, port.port_index, port.oid, port_attr['oper_status'])) + sai_thrift_set_port_attribute( + self.client, + port_oid=port.oid, + admin_state=True) + if not port_up: + down_port_list.append(index) if down_port_list: print("Ports {} are down after retries.".format(down_port_list)) return test_port_list diff --git a/test/sai_test/config/route_configer.py b/test/sai_test/config/route_configer.py index 0996333cc..2e88b42d1 100644 --- a/test/sai_test/config/route_configer.py +++ b/test/sai_test/config/route_configer.py @@ -639,9 +639,13 @@ def create_nexthop_group_by_nexthops(self, nexthopv4_list: List[Nexthop], nextho nhp_grpv4_members.append(nhp_grpv4_member) nhp_grpv6_members.append(nhp_grpv6_member) + # Give each NHG its own copy of the member port-index list. The v4 and v6 + # groups are mutated independently (remove/re-add member tests), so sharing + # one list object would let a v4 mutation corrupt the v6 group's port list + # (ValueError: x not in list) when both run in the same config group. member_port_indexs = [17, 18, 19, 20, 21, 22, 23, 24] - nhp_grpv4: NexthopGroup = NexthopGroup(nhop_groupv4_id, nhp_grpv4_members, member_port_indexs) - nhp_grpv6: NexthopGroup = NexthopGroup(nhop_groupv6_id, nhp_grpv6_members, member_port_indexs) + nhp_grpv4: NexthopGroup = NexthopGroup(nhop_groupv4_id, nhp_grpv4_members, list(member_port_indexs)) + nhp_grpv6: NexthopGroup = NexthopGroup(nhop_groupv6_id, nhp_grpv6_members, list(member_port_indexs)) self.test_obj.dut.nhp_grpv4_list.append(nhp_grpv4) self.test_obj.dut.nhp_grpv6_list.append(nhp_grpv6) From 464a48604dd8c94cc70475a70eb44d5436d77b1f Mon Sep 17 00:00:00 2001 From: Prince George <45705344+prgeor@users.noreply.github.com> Date: Sat, 25 Jul 2026 06:27:19 -0700 Subject: [PATCH 08/11] APSU SAI port attributes (#2303) Signed-off-by: Prince George --- doc/APSU/SAI-APSU-ILT.md | 188 +++++++++++++++++++++++++++++++++++++++ inc/saiport.h | 154 ++++++++++++++++++++++++++++++++ inc/saitypes.h | 37 ++++++++ meta/acronyms.txt | 3 + meta/saimetadatatypes.h | 5 ++ meta/saisanitycheck.c | 2 + 6 files changed, 389 insertions(+) create mode 100644 doc/APSU/SAI-APSU-ILT.md diff --git a/doc/APSU/SAI-APSU-ILT.md b/doc/APSU/SAI-APSU-ILT.md new file mode 100644 index 000000000..a6a305e57 --- /dev/null +++ b/doc/APSU/SAI-APSU-ILT.md @@ -0,0 +1,188 @@ +# SAI APSU / ILT Attributes + +## Overview + +This document maps the SAI APSU/ILT port attributes to their normative definitions in **IEEE P802.3dj Annex 178B** (*Autonomous PHY Start-Up (APSU) and In-band Link Training (ILT)*). + +APSU defines a procedure by which two connected PHY interfaces autonomously bring up a link — including in-band link training (ILT) and a Ready-to-Send (RTS) handshake — without requiring out-of-band coordination. The procedure applies to both the host interface (C2M AUI) and the media interface (PMD to medium). + +**Reference specification:** IEEE P802.3dj/D3.1, Annex 178B (4 June 2026 draft) + +--- + +## Architecture Summary + +``` +Per-LANE variables (independent per lane): + local_tf_lock — local receiver has identified training frame boundaries + remote_tf_lock — peer receiver has identified training frame boundaries (from status field) + local_rx_ready — local receiver fully adapted, ready for data + remote_rx_ready — peer receiver fully adapted (from status field) + training_failure — training failed to complete on this lane + lane_training_status — aggregate per-lane training state (IN_PROGRESS/TRAINED/OK/FAIL) + +Per-INTERFACE variables (aggregated across all lanes): + training_status — FAIL/IN_PROGRESS/READY/OK + isl_ready — local_rx_ready AND remote_rx_ready true for ALL lanes + local_rts — this interface is ready to send data + remote_rts — peer interface is ready to send (all peer lanes have NRTS=0) + rts_status — FAIL/IN_PROGRESS/READY/OK + +Management variables (per interface): + mr_training_enable — enables/disables ILT training on the interface + mr_restart — triggers a restart of the RTS and ILT functions +``` + +--- + +## Enum Definitions + +### `sai_port_ilt_training_status_t` + +Maps to the IEEE `training_status` per-interface variable (§178B.8.2.1, MDIO 1.1476.11:10). + +| SAI Enum Value | IEEE Value | Description | +|---|---|---| +| `SAI_PORT_ILT_TRAINING_STATUS_RESERVED` | `—` | Unknown / not applicable | +| `SAI_PORT_ILT_TRAINING_STATUS_FAIL` | `FAIL` | At least one lane has `training_failure = true` | +| `SAI_PORT_ILT_TRAINING_STATUS_IN_PROGRESS` | `IN_PROGRESS` | At least one lane is training; no lanes have failed | +| `SAI_PORT_ILT_TRAINING_STATUS_READY` | `READY` | All lanes are TRAINED or OK with at least one TRAINED | +| `SAI_PORT_ILT_TRAINING_STATUS_OK` | `OK` | All lanes are OK (fully trained and stable) | + +**Aggregation logic (§178B.8.2.1):** +- `FAIL` if ANY lane has `lane_training_status = FAIL` +- `IN_PROGRESS` if ≥1 lane is `IN_PROGRESS` and no lane is `FAIL` +- `READY` if ALL lanes are `TRAINED` or `OK` and ≥1 lane is `TRAINED` +- `OK` if ALL lanes are `OK` + +### `sai_port_ilt_rts_status_t` + +Maps to the IEEE `rts_status` per-interface variable (§178B.8.2.1, MDIO 1.1476.14:13). + +| SAI Enum Value | IEEE Value | Description | +|---|---|---| +| `SAI_PORT_ILT_RTS_STATUS_RESERVED` | `—` | Unknown / not applicable | +| `SAI_PORT_ILT_RTS_STATUS_FAIL` | `FAIL` | `training_status = FAIL`; RTS cannot proceed | +| `SAI_PORT_ILT_RTS_STATUS_IN_PROGRESS` | `IN_PROGRESS` | RTS handshake in progress | +| `SAI_PORT_ILT_RTS_STATUS_READY` | `READY` | Local RTS set; awaiting remote RTS | +| `SAI_PORT_ILT_RTS_STATUS_OK` | `OK` | Both local and remote RTS established; ISL in DATA mode | + +### `sai_port_ilt_lane_training_status_t` + +Maps to the IEEE `lane_training_status` per-lane variable (§178B.8.3.1, MDIO 1.1477.1:0). + +| SAI Enum Value | IEEE Value | Description | +|---|---|---| +| `SAI_PORT_ILT_LANE_TRAINING_STATUS_RESERVED` | `—` | Unknown / not applicable | +| `SAI_PORT_ILT_LANE_TRAINING_STATUS_FAIL` | `FAIL` | Training failed on this lane | +| `SAI_PORT_ILT_LANE_TRAINING_STATUS_IN_PROGRESS` | `IN_PROGRESS` | Lane is actively training | +| `SAI_PORT_ILT_LANE_TRAINING_STATUS_TRAINED` | `TRAINED` | Lane completed training; both sides RX ready | +| `SAI_PORT_ILT_LANE_TRAINING_STATUS_OK` | `OK` | Lane is in DATA mode and operational | + +--- + +## SAI Port Attribute to IEEE 802.3dj Variable Mapping + +### Control Attributes + +| SAI Attribute | IEEE Variable | IEEE Section | MDIO Register (PMD) | MDIO Ref | Granularity | SAI Type | +|---|---|---|---|---|---|---| +| `SAI_PORT_ATTR_APSU_MR_TRAINING_ENABLE` | `mr_training_enable` | §178B.8.2.1 | `1.150.1` | 45.2.1.95 | Per interface | `bool` (default `true`) | +| `SAI_PORT_ATTR_APSU_MR_RESTART` | `mr_restart` | §178B.8.2.1 | `1.150.0` | 45.2.1.95 | Per interface | `bool` (self-clearing, default `false`) | + +**`mr_training_enable`** (§178B.8.2.1): When `true`, ILT training is enabled on the interface. Must be set to the same value at both ends of each ISL. Disabling training on ILT-capable ISLs may compromise APSU and link performance. + +**`mr_restart`** (§178B.8.2.1): When set to `true`, triggers a restart of both the RTS and ILT state machines on the interface. The attribute is self-clearing — after the restart is initiated, hardware clears the bit, allowing subsequent restarts to be triggered by writing `true` again. + +--- + +### Per-Interface Status Attributes + +| SAI Attribute | IEEE Variable | IEEE Section | MDIO Register (PMD) | MDIO Ref | SAI Type | +|---|---|---|---|---|---| +| `SAI_PORT_ATTR_ILT_TRAINING_STATUS` | `training_status` | §178B.8.2.1 | `1.1476.11:10` | 45.2.1.168c | `sai_port_ilt_training_status_t` | +| `SAI_PORT_ATTR_ILT_RTS_STATUS` | `rts_status` | §178B.8.2.1 | `1.1476.14:13` | 45.2.1.168c | `sai_port_ilt_rts_status_t` | +| `SAI_PORT_ATTR_APSU_LOCAL_RTS_STATUS` | `local_rts` | §178B.8.2.1 | `1.1320.10` | 45.2.1.165 | `sai_latch_status_t` | +| `SAI_PORT_ATTR_APSU_REMOTE_RTS_STATUS` | `remote_rts` | §178B.8.2.1 | `1.1120.10` | 45.2.1.161 | `sai_latch_status_t` | +| `SAI_PORT_ATTR_APSU_ISL_READY` | `isl_ready` | §178B.8.2.1 | `1.1476.8` | 45.2.1.168c | `sai_latch_status_t` | + +**`training_status`**: Per-interface aggregation of all per-lane `lane_training_status` values. See enum table above. + +**`rts_status`**: Tracks the RTS handshake state machine. Transitions from `IN_PROGRESS` → `READY` (local RTS set) → `OK` (remote RTS received, ISL enters DATA mode). Set to `FAIL` if `training_status = FAIL`. + +**`local_rts`**: Single boolean per interface. Set to `true` after all lanes reach `TRAINED`/`OK` and the clock swap completes. Its logical-NOT is encoded as the **Not Ready to Send (NRTS)** bit in the control field of every transmitted training frame (§178B.7.4.2). + +**`remote_rts`**: Set to `true` when received training frames on **all lanes** of the interface have NRTS bit = 0. Represents `local_rts` of the peer interface as observed locally. + +**`isl_ready`**: Set to `true` when `local_rx_ready` AND `remote_rx_ready` are `true` for **all lanes** of the interface (§178B.8.2.1). Triggers the RTS state machine. This is the primary observable indicator that bidirectional training has converged on all lanes. + +--- + +### Per-Lane Status Attributes + +The following attributes return a list of per-lane values (`sai_port_lane_latch_status_list_t`), with one entry per lane of the interface. + +| SAI Attribute | IEEE Variable | IEEE Section | MDIO Register (PMD, lane 0) | MDIO Ref | SAI Type | +|---|---|---|---|---|---| +| `SAI_PORT_ATTR_APSU_LOCAL_TF_LOCK_STATUS` | `local_tf_lock` | §178B.8.3.1 | `1.151.1` (lane 0–3); see 45.2.1.96 for lanes 4–7 | 45.2.1.96 | `sai_port_lane_latch_status_list_t` | +| `SAI_PORT_ATTR_APSU_REMOTE_TF_LOCK_STATUS` | `remote_tf_lock` | §178B.8.3.1 | `1.1420.9` (lane 0; lanes 1–7 offset in same reg) | 45.2.1.167 | `sai_port_lane_latch_status_list_t` | +| `SAI_PORT_ATTR_APSU_LOCAL_RX_READY_STATUS` | `local_rx_ready` | §178B.8.3.1 | `1.1420.15` (lane 0; lanes 1–7 offset in same reg) | 45.2.1.167 | `sai_port_lane_latch_status_list_t` | +| `SAI_PORT_ATTR_APSU_REMOTE_RX_READY_STATUS` | `remote_rx_ready` | §178B.8.3.1 | `1.1220.15` (lane 0; lanes 1–7 offset in same reg) | 45.2.1.163 | `sai_port_lane_latch_status_list_t` | +| `SAI_PORT_ATTR_APSU_LOCAL_TRAINING_FAILURE_STATUS` | `training_failure` | §178B.8.3.1 | `1.151.3` (lane 0–3); see 45.2.1.101 for lanes 4–7 | 45.2.1.96 | `sai_port_lane_latch_status_list_t` | + +> **Note on MDIO lane addressing:** Register bit references above are for lane 0 on a PMD. Lanes 1–7 are accessed at defined offsets within the same register (footnote c in Table 178B–7). Mappings for upper/lower AUI components are defined in §176C.8 and §176D.9 respectively. + +**`local_tf_lock`** (§178B.8.3.1): `true` when the training frame marker position has been identified on a lane. Set by the training frame lock state diagram (Figure 178B–11). Reflected in the **Receiver frame lock** bit (bit 9) of the training frame status field (§178B.7.5.4), making it observable by the peer as `remote_tf_lock`. + +**`remote_tf_lock`** (§178B.8.3.1): Reflects `local_tf_lock` of the peer on this lane. Derived from the Receiver frame lock bit (bit 9) of received training frames (Table 178B–4, Table 178B–5). + +**`local_rx_ready`** (§178B.8.3.1): `true` when the receiver on a lane has completed equalization and is ready for data. Set by the training control state diagram (Figure 178B–10a). Encoded as the **Receiver ready** bit (bit 15) of the training frame status field (§178B.7.5.1). + +**`remote_rx_ready`** (§178B.8.3.1): Reflects `local_rx_ready` of the peer on this lane. Derived from the Receiver ready bit (bit 15) of received training frames. `isl_ready` is `true` when this is `true` on all lanes. + +**`training_failure`** (§178B.8.3.1): `true` when training fails to complete on a lane, typically due to `max_wait_timer` expiry. Set by the training control state diagram (Figure 178B–10a). When `true` on any lane, it causes `training_status = FAIL` and `rts_status = FAIL` at the interface level. + +> **Observability note:** There is no `remote_training_failure` variable in IEEE 802.3dj. A remote-side training failure is only indirectly observable: it causes `remote_rx_ready` to remain `false`, which prevents `isl_ready` from being set, which causes the local `max_wait_timer` to expire and sets the local `training_failure = true`. Software cannot distinguish between a local-only failure and a remote-triggered timeout from these attributes alone. + +--- + +## Variable Interaction and Bring-Up Sequence + +``` +For each lane i: + 1. Training frames exchanged (tx_mode = TRAINING) + 2. local_tf_lock[i] → true (frame boundaries found) + 3. remote_tf_lock[i] → true (peer reports frame lock in status field) + 4. Coefficient update / equalization runs + 5. local_rx_ready[i] → true (local adaptation complete) + 6. remote_rx_ready[i] → true (peer reports ready in status field) + OR training_failure[i] → true (max_wait_timer expired) + +Across all lanes (interface level): + 7. isl_ready → true (local_rx_ready AND remote_rx_ready for ALL lanes) + 8. local_rts → true (clock swap completes, NRTS bit set to 0 in all TX frames) + 9. remote_rts → true (all peer lanes have NRTS=0) + 10. rts_status → OK (tx_mode = DATA; live traffic) +``` + +--- + +## MDIO Register Summary (PMD, Annex 178B Tables 178B–6 and 178B–7) + +| IEEE Variable | MDIO Register (lane 0) | MDIO Ref | Control/Status | Granularity | +|---|---|---|---|---| +| `mr_restart` | `1.150.0` | 45.2.1.95 | Control | Per interface | +| `mr_training_enable` | `1.150.1` | 45.2.1.95 | Control | Per interface | +| `max_wait_timer_duration` | `1.1459.15:0` | 45.2.1.168a | Control | Per interface | +| `isl_ready` | `1.1476.8` | 45.2.1.168c | Status | Per interface | +| `rts_status` | `1.1476.14:13` | 45.2.1.168c | Status | Per interface | +| `training_status` | `1.1476.11:10` | 45.2.1.168c | Status | Per interface | +| `local_rts` | `1.1320.10` | 45.2.1.165 | Status | Per interface | +| `remote_rts` | `1.1120.10` | 45.2.1.161 | Status | Per interface | +| `lane_training_status` | `1.1477.1:0` | 45.2.1.168d | Status | Per lane | +| `local_tf_lock` | `1.151.1` (lanes 0–3) | 45.2.1.96 | Status | Per lane | +| `remote_tf_lock` | `1.1420.9` | 45.2.1.167 | Status | Per lane | +| `local_rx_ready` | `1.1420.15` | 45.2.1.167 | Status | Per lane | +| `remote_rx_ready` | `1.1220.15` | 45.2.1.163 | Status | Per lane | +| `training_failure` | `1.151.3` (lanes 0–3) | 45.2.1.96 | Status | Per lane | +| `polarity_correction` | `1.1476.0` | 45.2.1.168c | Status | Per lane | diff --git a/inc/saiport.h b/inc/saiport.h index 8c14e8ad2..a200e7572 100644 --- a/inc/saiport.h +++ b/inc/saiport.h @@ -869,6 +869,50 @@ typedef enum _sai_llr_frame_action_t } sai_llr_frame_action_t; +/** + * @brief Interface RTS status + */ +typedef enum _sai_port_ilt_rts_status_t +{ + /** RTS status is unknown */ + SAI_PORT_ILT_RTS_STATUS_RESERVED, + + /** RTS status is fail */ + SAI_PORT_ILT_RTS_STATUS_FAIL, + + /** RTS status is in progress */ + SAI_PORT_ILT_RTS_STATUS_IN_PROGRESS, + + /** RTS status is Ready */ + SAI_PORT_ILT_RTS_STATUS_READY, + + /** RTS status is OK */ + SAI_PORT_ILT_RTS_STATUS_OK + +} sai_port_ilt_rts_status_t; + +/** + * @brief Interface ILT Training status + */ +typedef enum _sai_port_ilt_training_status_t +{ + /** Training status is reserved */ + SAI_PORT_ILT_TRAINING_STATUS_RESERVED, + + /** Training status is failed */ + SAI_PORT_ILT_TRAINING_STATUS_FAIL, + + /** Training status is in progress */ + SAI_PORT_ILT_TRAINING_STATUS_IN_PROGRESS, + + /** Training status is Ready */ + SAI_PORT_ILT_TRAINING_STATUS_READY, + + /** Training status is OK */ + SAI_PORT_ILT_TRAINING_STATUS_OK + +} sai_port_ilt_training_status_t; + /** * @brief Attribute Id in sai_set_port_attribute() and * sai_get_port_attribute() calls @@ -3327,6 +3371,116 @@ typedef enum _sai_port_attr_t */ SAI_PORT_ATTR_UNKNOWN_MULTICAST_STORM_CONTROL_POLICER_ID, + /** + * @brief Interface ILT Training status + * + * @type sai_port_ilt_training_status_t + * @flags READ_ONLY + */ + SAI_PORT_ATTR_ILT_TRAINING_STATUS, + + /** + * @brief ILT per-lane training status + * + * Per-lane ILT training state. + * + * @type sai_port_ilt_lane_training_status_list_t + * @flags READ_ONLY + */ + SAI_PORT_ATTR_ILT_LANE_TRAINING_STATUS_LIST, + + /** + * @brief Interface RTS status + * + * @type sai_port_ilt_rts_status_t + * @flags READ_ONLY + */ + SAI_PORT_ATTR_ILT_RTS_STATUS, + + /** + * @brief APSU MR mr_training_enable variable to enable/disable the training on the interface + * + * @type bool + * @flags CREATE_AND_SET + * @default true + */ + SAI_PORT_ATTR_APSU_MR_TRAINING_ENABLE, + + /** + * @brief APSU mr_restart variable to restart the RTS and ILT functions when true + * + * The variable should be self-clearing so that RTS+ILT functions can be restarted multiple times + * + * @type bool + * @flags CREATE_AND_SET + * @default false + */ + SAI_PORT_ATTR_APSU_MR_RESTART, + + /** + * @brief APSU local_tf_lock variable + * + * @type sai_port_lane_latch_status_list_t + * @flags READ_ONLY + */ + SAI_PORT_ATTR_APSU_LOCAL_TF_LOCK_STATUS, + + /** + * @brief APSU remote_tf_lock variable + * + * @type sai_port_lane_latch_status_list_t + * @flags READ_ONLY + */ + SAI_PORT_ATTR_APSU_REMOTE_TF_LOCK_STATUS, + + /** + * @brief APSU local_rx_ready variable + * + * @type sai_port_lane_latch_status_list_t + * @flags READ_ONLY + */ + SAI_PORT_ATTR_APSU_LOCAL_RX_READY_STATUS, + + /** + * @brief APSU remote_rx_ready variable + * + * @type sai_port_lane_latch_status_list_t + * @flags READ_ONLY + */ + SAI_PORT_ATTR_APSU_REMOTE_RX_READY_STATUS, + + /** + * @brief Per lane ILT training_failure variable + * + * @type sai_port_lane_latch_status_list_t + * @flags READ_ONLY + */ + SAI_PORT_ATTR_ILT_TRAINING_FAILURE, + + /** + * @brief APSU local_rts variable for the interface + * + * @type sai_latch_status_t + * @flags READ_ONLY + */ + SAI_PORT_ATTR_APSU_LOCAL_RTS_STATUS, + + /** + * @brief APSU remote_rts variable for the interface + * + * @type sai_latch_status_t + * @flags READ_ONLY + */ + SAI_PORT_ATTR_APSU_REMOTE_RTS_STATUS, + + /** + * @brief APSU isl_ready variable for the interface + * + * @type sai_latch_status_t + * @flags READ_ONLY + */ + SAI_PORT_ATTR_APSU_ISL_READY, + /** * @brief End of attributes */ diff --git a/inc/saitypes.h b/inc/saitypes.h index 3d3c6a7a5..168e3de72 100644 --- a/inc/saitypes.h +++ b/inc/saitypes.h @@ -570,6 +570,40 @@ typedef struct _sai_prbs_per_lane_rx_state_list_t sai_prbs_per_lane_rx_state_t *list; } sai_prbs_per_lane_rx_state_list_t; +/** + * @brief Per-lane ILT training status + */ +typedef enum _sai_port_ilt_lane_training_status_t +{ + /** Lane training status is reserved */ + SAI_PORT_ILT_LANE_TRAINING_STATUS_RESERVED, + + /** Lane training failed (training_failure true on this lane) */ + SAI_PORT_ILT_LANE_TRAINING_STATUS_FAIL, + + /** Lane training is in progress */ + SAI_PORT_ILT_LANE_TRAINING_STATUS_IN_PROGRESS, + + /** Lane training completed */ + SAI_PORT_ILT_LANE_TRAINING_STATUS_TRAINED, + + /** Lane training fully trained */ + SAI_PORT_ILT_LANE_TRAINING_STATUS_OK, + +} sai_port_ilt_lane_training_status_t; + +typedef struct _sai_ilt_lane_training_status_t +{ + uint32_t lane; + sai_port_ilt_lane_training_status_t training_status; +} sai_ilt_lane_training_status_t; + +typedef struct _sai_port_ilt_lane_training_status_list_t +{ + uint32_t count; + sai_ilt_lane_training_status_t *list; +} sai_port_ilt_lane_training_status_list_t; + /** * @brief Field match mask * @@ -1807,6 +1841,9 @@ typedef union _sai_attribute_value_t /** @validonly meta->attrvaluetype == SAI_ATTR_VALUE_TYPE_UINT64_RANGE_LIST */ sai_u64_range_list_t u64rangelist; + /** @validonly meta->attrvaluetype == SAI_ATTR_VALUE_TYPE_PORT_ILT_LANE_TRAINING_STATUS_LIST */ + sai_port_ilt_lane_training_status_list_t port_ilt_lane_training_status_list; + } sai_attribute_value_t; /** diff --git a/meta/acronyms.txt b/meta/acronyms.txt index 180a7a6b6..8f4dee181 100644 --- a/meta/acronyms.txt +++ b/meta/acronyms.txt @@ -5,6 +5,7 @@ AES - Advanced Encryption Standard AN - Association Number AN - Auto Negotiation API - Application Program Interface +APSU - Autonomous Path Start Up ARP - Address Resolution Protocol ARS - Adaptiver Routing and Switching ASIC - Application Specific Integrated Circuit @@ -85,6 +86,7 @@ IFA1 - Inband Flow Analyzer version 1 IFA2 - Inband Flow Analyzer version 2 IGMP - Internet Group Management Protocol IHL - Internet Header Length +ILT - Inter-sublayer Link Training IOAM - In-situ Operations And Management IP - Internet Protocol IP2ME - IP packets to local router IP @@ -147,6 +149,7 @@ RPC - Remote Procedure Call RPF - Reverse Path Forwarding RPN - Reverse Polish Notation RSPAN - Remote Span +RTS - Ready To Send SA - Secure Association SAI - Switch Abstraction Interface SAK - Secure Association Key diff --git a/meta/saimetadatatypes.h b/meta/saimetadatatypes.h index 16d5edd89..27b0d05c7 100644 --- a/meta/saimetadatatypes.h +++ b/meta/saimetadatatypes.h @@ -531,6 +531,11 @@ typedef enum _sai_attr_value_type_t */ SAI_ATTR_VALUE_TYPE_PRBS_PER_LANE_BIT_ERROR_RATE_LIST, + /** + * @brief Attribute value is a list of ILT lane training status for all lanes in a port. + */ + SAI_ATTR_VALUE_TYPE_PORT_ILT_LANE_TRAINING_STATUS_LIST, + /** * @brief Attribute value is 64 bit unsigned integer range. */ diff --git a/meta/saisanitycheck.c b/meta/saisanitycheck.c index 2728df71f..87943443c 100644 --- a/meta/saisanitycheck.c +++ b/meta/saisanitycheck.c @@ -856,6 +856,7 @@ void check_attr_object_type_provided( case SAI_ATTR_VALUE_TYPE_PRBS_PER_LANE_RX_STATE_LIST: case SAI_ATTR_VALUE_TYPE_PRBS_BIT_ERROR_RATE: case SAI_ATTR_VALUE_TYPE_PRBS_PER_LANE_BIT_ERROR_RATE_LIST: + case SAI_ATTR_VALUE_TYPE_PORT_ILT_LANE_TRAINING_STATUS_LIST: if (md->allowedobjecttypes != NULL) { @@ -3006,6 +3007,7 @@ void check_attr_is_primitive( case SAI_ATTR_VALUE_TYPE_PRBS_PER_LANE_RX_STATUS_LIST: case SAI_ATTR_VALUE_TYPE_PRBS_PER_LANE_RX_STATE_LIST: case SAI_ATTR_VALUE_TYPE_PRBS_PER_LANE_BIT_ERROR_RATE_LIST: + case SAI_ATTR_VALUE_TYPE_PORT_ILT_LANE_TRAINING_STATUS_LIST: if (md->isprimitive) { From 37af762edac9f565a1d74c828a05ef0ac2868964 Mon Sep 17 00:00:00 2001 From: Nicholas <126026386+nicholasching@users.noreply.github.com> Date: Tue, 28 Jul 2026 11:56:46 -0400 Subject: [PATCH 09/11] [sai_test]: Isolate simulated IPv6 startup control traffic- (#2321) Context / motivation Part of the SAIVPP unit-test framework. When OCP sai_test runs on a standalone bench with SIMULATE_SONIC=1, the bench emulates SONiC control-plane setup for LAG/SVI router interfaces. During that setup, a simulated router can emit IPv6 lifecycle frames such as Router Solicitations and MLDv2 reports. These frames share PTF dataplane queues with test traffic and can make strict verify_no_other_packets() assertions fail even though the test's TCP/UDP forwarding behavior is correct. The sonic-sairedis harness previously used PTF --relax globally, which disabled all unexpected-packet checks and masked this issue. The harness now scopes --relax to positive flooding tests only, so the shared simulation helper needs a narrow, opt-in way to isolate known startup control traffic without suppressing actual dataplane failures. What this change does test/sai_test/config/simulate_sonic.py. Adds an opt-in PTF filter enabled only when SIMULATE_SONIC=1 and a caller provides SIMULATE_SONIC_IPV6_CONTROL_SRC_MAC. Exact filtering. Discards only IPv6 ICMP Router Solicitations (type 133 to ff02::2) and MLDv2 reports (type 143 to ff02::16, including the standard Hop-by-Hop header) emitted from that configured source MAC. No vendor hardcoding. The caller supplies the source MAC. The shared helper does not contain VPP-specific MAC addresses or commands. Linux IPv6 setup ordering. Configures accept_dad=0, accept_ra=0, autoconf=0, and router_solicitations=0 before re-enabling IPv6 on a simulated LAG host interface, preventing a DAD/router-discovery race. Scope / risk One shared test-config helper. No SAI headers, backend code, or production SONiC runtime behavior changes. Default-off: with SIMULATE_SONIC unset, or without a valid source MAC, the helper installs no filter and retains existing behavior. Narrow by construction: ordinary IPv4/IPv6 traffic, other ICMPv6 types, and matching control frames from other source MACs stay visible to PTF strict verification. Validated behavior: unit-level boundary checks used captured Router Solicitation and MLDv2 frames and confirmed that ordinary traffic is retained. A standalone VPP harness rerun recovered all eleven classes previously failing solely on background IPv6 lifecycle traffic. Dependencies None. The change extends the existing SIMULATE_SONIC helper implemented in #2316 and is independent of the prior SIMULATE_SONIC PR once that PR is merged. The sonic-sairedis VPP harness enables the option only in its separate Phase 3 integration branch. Signed-off-by: Nicholas Ching z --- test/sai_test/config/simulate_sonic.py | 90 +++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 9 deletions(-) diff --git a/test/sai_test/config/simulate_sonic.py b/test/sai_test/config/simulate_sonic.py index 1a9eb3975..300b7dbf5 100644 --- a/test/sai_test/config/simulate_sonic.py +++ b/test/sai_test/config/simulate_sonic.py @@ -42,12 +42,71 @@ import time _logger = logging.getLogger(__name__) +_IPV6_CONTROL_FILTER_INSTALLED = False +_ROUTER_SOLICITATION_DESTINATION = bytes.fromhex("ff020000000000000000000000000002") +_MLDV2_REPORT_DESTINATION = bytes.fromhex("ff020000000000000000000000000016") def enabled(): return os.environ.get("SIMULATE_SONIC", "0") == "1" +def _is_ipv6_control_packet(packet_data, source_mac): + """Return whether packet_data is a simulated router RS or MLDv2 frame.""" + packet_bytes = bytes(packet_data) + ethernet_length = 14 + ipv6_header_length = 40 + payload_offset = ethernet_length + ipv6_header_length + + if len(packet_bytes) <= payload_offset: + return False + if packet_bytes[6:12] != source_mac or packet_bytes[12:14] != b"\x86\xdd": + return False + + destination = packet_bytes[ethernet_length + 24:ethernet_length + 40] + next_header = packet_bytes[ethernet_length + 6] + if next_header == 0: + if len(packet_bytes) < payload_offset + 2: + return False + next_header = packet_bytes[payload_offset] + payload_offset += (packet_bytes[payload_offset + 1] + 1) * 8 + if next_header != 58 or len(packet_bytes) <= payload_offset: + return False + + icmpv6_type = packet_bytes[payload_offset] + return ( + destination == _ROUTER_SOLICITATION_DESTINATION and icmpv6_type == 133 + ) or ( + destination == _MLDV2_REPORT_DESTINATION and icmpv6_type == 143 + ) + + +def install_ipv6_control_filter(): + """Discard simulated-router IPv6 startup frames when the harness opts in.""" + global _IPV6_CONTROL_FILTER_INSTALLED + + source_mac = os.environ.get("SIMULATE_SONIC_IPV6_CONTROL_SRC_MAC", "") + if _IPV6_CONTROL_FILTER_INSTALLED or not enabled() or not source_mac: + return + + from ptf.testutils import add_filter + + try: + source_mac_bytes = bytes.fromhex(source_mac.replace(":", "")) + except ValueError: + _logger.warning("invalid SIMULATE_SONIC_IPV6_CONTROL_SRC_MAC: %s", source_mac) + return + if len(source_mac_bytes) != 6: + _logger.warning("invalid SIMULATE_SONIC_IPV6_CONTROL_SRC_MAC: %s", source_mac) + return + + def keep_non_control_packet(packet_data): + return not _is_ipv6_control_packet(packet_data, source_mac_bytes) + + add_filter(keep_non_control_packet) + _IPV6_CONTROL_FILTER_INSTALLED = True + + def _run(cmd, check=False, quiet=False): result = subprocess.run(cmd, check=False, capture_output=True, text=True) if result.returncode != 0 and not quiet: @@ -79,6 +138,8 @@ def ensure_portchannel(lag_index): if not enabled(): return + install_ipv6_control_filter() + pc_if = "PortChannel{}".format(lag_index) mtu = _mtu() @@ -99,6 +160,8 @@ def assign_lag_rif_ips(lag_index, retries=20, interval=0.3): if not enabled() or os.environ.get("LAG_RIF_IPS", "1") != "1": return + install_ipv6_control_filter() + be_prefix = os.environ.get("LAG_BE_TAP_PREFIX", "be") v4_pattern = os.environ.get("LAG_RIF_IPV4_PATTERN", "10.1.%d.1/24") v6_pattern = os.environ.get("LAG_RIF_IPV6_PATTERN", "fc00:1::%d:1/112") @@ -114,15 +177,22 @@ def assign_lag_rif_ips(lag_index, retries=20, interval=0.3): else: return - disable_v6 = "/proc/sys/net/ipv6/conf/{}/disable_ipv6".format(be_if) - accept_dad = "/proc/sys/net/ipv6/conf/{}/accept_dad".format(be_if) - try: - with open(disable_v6, "w", encoding="ascii") as fh: - fh.write("0") - with open(accept_dad, "w", encoding="ascii") as fh: - fh.write("0") - except OSError: - pass + ipv6_conf = "/proc/sys/net/ipv6/conf/{}".format(be_if) + # Re-enabling IPv6 creates the link-local address immediately, so apply all + # host-control settings first to prevent DAD and router discovery packets. + settings = ( + ("accept_dad", "0"), + ("accept_ra", "0"), + ("autoconf", "0"), + ("router_solicitations", "0"), + ("disable_ipv6", "0"), + ) + for setting, value in settings: + try: + with open(os.path.join(ipv6_conf, setting), "w", encoding="ascii") as fh: + fh.write(value) + except OSError: + pass _run(["ip", "addr", "add", v4_addr, "dev", be_if], check=False) _run(["ip", "-6", "addr", "add", v6_addr, "dev", be_if, "nodad"], check=False) @@ -148,6 +218,8 @@ def assign_svi_rif_ips(vlan_id, retries=20, interval=0.3): if not enabled() or os.environ.get("SVI_RIF_IPS", "1") != "1": return + install_ipv6_control_filter() + set_ip_cmd = os.environ.get("SVI_RIF_SET_IP_CMD") if not set_ip_cmd: return From 1628c02a59c661d1ae15c39f043a8518aaf3111a Mon Sep 17 00:00:00 2001 From: Nathan Ni Date: Wed, 29 Jul 2026 21:43:17 +0800 Subject: [PATCH 10/11] add SAI definitions for optical circuit switch (#2229) * add SAI definitions for optical circuit switch Signed-off-by: Nathan Ni --- doc/OCS/SAI-Proposal-OCS.md | 313 +++++++++++++++ doc/OCS/images/port_status.png | Bin 0 -> 28207 bytes experimental/saiexperimentalocs.h | 598 +++++++++++++++++++++++++++++ experimental/saiextensions.h | 3 + experimental/saiswitchextensions.h | 11 + experimental/saitypesextensions.h | 6 + inc/saiobject.h | 1 + meta/acronyms.txt | 1 + 8 files changed, 933 insertions(+) create mode 100755 doc/OCS/SAI-Proposal-OCS.md create mode 100755 doc/OCS/images/port_status.png create mode 100644 experimental/saiexperimentalocs.h diff --git a/doc/OCS/SAI-Proposal-OCS.md b/doc/OCS/SAI-Proposal-OCS.md new file mode 100755 index 000000000..2fb36ef75 --- /dev/null +++ b/doc/OCS/SAI-Proposal-OCS.md @@ -0,0 +1,313 @@ +SAI Optical Circuit Switch API Proposal +===================== + +Title | SAI Optical Circuit Switch API Proposal +------------|---------------------- +Authors | Coherent +Status | Draft +Type | Standards track +Created | 11/20/2025 +SAI-Version | ? +------------------------------- + +# 1. Scope + +This document defines the draft technical specifications for the API in Open Compute Project Switch Abstraction Interface (SAI) used to support the configurations of OCS port and OCS cross connect in the OCS device, as well as the retrieval of OCS operational status data from OCS device. + +# 2. Overview +As AI clusters reach massive proportions, Optical Circuit Switching (OCS) is becoming essential. It slashes energy overhead, reduces network complexity, and paves the way for revolutionary data center designs and applications. OCS device can have varying capacities from different hardware or module, defined as "number of A side ports" × "number of B side ports" (e.g., 64×64, 320×320, etc.). OCS cross-connect is direct optical transmission channel between different optical links, it is provisioned between port on one side (e.g. A side) and port on the other side (e.g. B side). + +Main functions of the OCS SAI definitions include: +* Support creation and remove of OCS cross-connects, including both individual switching and simultaneous switching with multiple connections. +* Support OCS ports operation, including the ability to set a state to any specified group of ports. +* Support the retrieval of current operational status on OCS port. +* Support the retrieval of hardware-specific OCS module data, such as port physical mapping information and factory insertion loss values etc. + +# 3. OCS Port +## 3.1 OCS Port Discovery and Creation +* The capacity of OCS device (number of A side ports and number of B side ports) and corresponding default port configurations are defined in OCS device specific template/profile, example as below: +```cpp +# name state +OCS_PORT|1A normal +OCS_PORT|1B normal +OCS_PORT|2A normal +OCS_PORT|2B normal +OCS_PORT|3A normal +OCS_PORT|3B normal +OCS_PORT|4A normal +OCS_PORT|4B normal +OCS_PORT|5A normal +OCS_PORT|5B normal +OCS_PORT|6A normal +OCS_PORT|6B normal +OCS_PORT|7A normal +OCS_PORT|7B normal +OCS_PORT|8A normal +OCS_PORT|8B normal +... +``` +* OCS ports are created by upstream application using SAI API by parsing OCS device capability specific template/profile. +* After creation, OCS port is not deletable. +## 3.2 OCS Port Configuration +* The key configurable parameter is the override state (normal or force-blocked, etc.). +* Override state configuration can be changed regardless wether there's OCS cross-connect on the port. +## 3.3 OCS Port Operational Status +The OCS port operational status relies on the override state and the presence of an OCS cross-connect configuration on the port. + +![OCS Port Operational Status](images/port_status.png) +The OCS port physical mapping information is about the internal port mapping to faceplate ports. +# 4. OCS Cross-Connects +OCS cross-connects are dynamically created and removed via SAI APIs. +## 4.1. Object Relationships +* The SAI object IDs of OCS ports serve as the SAI attributes for both the ingress and egress ports in OCS cross-connect SAI object. +* OCS ports are referenced to OCS cross-connect by their SAI object IDs in cross-connect attributes. +## 4.2. Cross-Connect Configuration +* OCS cross-connect can be provisioned between two OCS ports such as 1A-1B, 1A-2A, etc. +* SAI bulk create/remove APIs are used for management of OCS cross-connect configurations. The backend logic under SAI can be implemented with one operation to OCS device, similar as overwrite behavior. +# 5. OCS Cross-Connects Factory Data +OCS cross-connect factory data are read-only information about factory beginning-of-life measurements that serve as a baseline reference that users can compare with. +* Factory data are measured during the manufacturing calibration phase. +* The measurement is for optical paths (cross-connects) with an optical spectrum (central frequency) in specific environment (temperature). + +# 6. OCS SAI Object and Attributes +| SAI Object | SAI Attributes | SAI Data Type | +| ---- | ---- | ---- | +|SAI_OBJECT_TYPE_OCS_CROSS_CONNECT | SAI_OCS_CROSS_CONNECT_ATTR_A_SIDE_PORT_ID | sai_object_id_t +| | SAI_OCS_CROSS_CONNECT_ATTR_B_SIDE_PORT_ID | sai_object_id_t +| SAI_OBJECT_TYPE_OCS_PORT | SAI_OCS_PORT_ATTR_NAME | sai_u8_list_t +| | SAI_OCS_PORT_ATTR_OVERRIDE_STATE | sai_ocs_port_override_state_t +| | SAI_OCS_PORT_ATTR_OPER_STATUS | sai_ocs_port_status_t +| | SAI_OCS_PORT_ATTR_PHYSICAL_MAPPING | sai_u8_list_t +SAI_OBJECT_TYPE_OCS_CROSS_CONNECT_FACTORY_DATA | SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_A_SIDE_PORT_NAME | sai_u8_list_t +| | SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_B_SIDE_PORT_NAME | sai_u8_list_t +| | SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_FREQUENCY_THZ | sai_s32_list_t +| | SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_MEASURED_TEMPERATURE | sai_s32_list_t +| | SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_INSERTION_LOSS_DB | sai_s32_list_t +| | SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_INSERTION_LOSS_ACCURACY_DB | sai_s32_list_t + +# 7. OCS SAI APIs +```cpp +** + * @brief OCS methods table retrieved with sai_api_query() + */ +typedef struct _sai_ocs_api_t +{ + sai_create_ocs_port_fn create_ocs_port; + sai_remove_ocs_port_fn remove_ocs_port; + sai_set_ocs_port_attribute_fn set_ocs_port_attribute; + sai_get_ocs_port_attribute_fn get_ocs_port_attribute; + sai_bulk_object_create_fn create_ocs_ports; + sai_bulk_object_remove_fn remove_ocs_ports; + sai_bulk_object_set_attribute_fn set_ocs_ports_attribute; + sai_bulk_object_get_attribute_fn get_ocs_ports_attribute; + sai_get_ocs_port_stats_fn get_ocs_port_stats; + sai_get_ocs_port_stats_ext_fn get_ocs_port_stats_ext; + sai_clear_ocs_port_stats_fn clear_ocs_port_stats; + sai_create_ocs_cross_connect_fn create_ocs_cross_connect; + sai_remove_ocs_cross_connect_fn remove_ocs_cross_connect; + sai_set_ocs_cross_connect_attribute_fn set_ocs_cross_connect_attribute; + sai_get_ocs_cross_connect_attribute_fn get_ocs_cross_connect_attribute; + sai_get_ocs_cross_connect_stats_fn get_ocs_cross_connect_stats; + sai_get_ocs_cross_connect_stats_ext_fn get_ocs_cross_connect_stats_ext; + sai_clear_ocs_cross_connect_stats_fn clear_ocs_cross_connect_stats; + sai_bulk_object_create_fn create_ocs_cross_connects; + sai_bulk_object_remove_fn remove_ocs_cross_connects; + sai_bulk_object_set_attribute_fn set_ocs_cross_connects_attribute; + sai_bulk_object_get_attribute_fn get_ocs_cross_connects_attribute; + sai_create_ocs_cross_connect_factory_data_fn create_ocs_cross_connect_factory_data; + sai_remove_ocs_cross_connect_factory_data_fn remove_ocs_cross_connect_factory_data; + sai_set_ocs_cross_connect_factory_data_attribute_fn set_ocs_cross_connect_factory_data_attribute; + sai_get_ocs_cross_connect_factory_data_attribute_fn get_ocs_cross_connect_factory_data_attribute; + sai_bulk_object_create_fn create_ocs_cross_connect_factory_datas; + sai_bulk_object_remove_fn remove_ocs_cross_connect_factory_datas; + sai_bulk_object_set_attribute_fn set_ocs_cross_connect_factory_datas_attribute; + sai_bulk_object_get_attribute_fn get_ocs_cross_connect_factory_datas_attribute; + +} sai_ocs_api_t; +``` + +# 8. OCS SAI API Usage Examples +## 8.1. Usage Example: Create OCS Ports +The following example demonstrates how to create OCS ports by specifying port name and port override state. Note: after OCS ports are created, the returned SAI object IDs will be buffered (_port_key_to_saiobjid_map in this example) and they will be used for following-up set/get operations on OCS ports. +```cpp + vector keys = {"1A", "1B", "2A", "2B", "3A", "3B"}; + + vector> attr_data_list; + vector attr_count_list; + vector attr_ptr_list; + + for (auto& key : keys) + { + sai_attribute_t attr; + vector attrs; + + attr.id = SAI_OCS_PORT_ATTR_NAME; + attr.value.u8list.count = (uint32_t)key.size(); + attr.value.u8list.list = (uint8_t*)const_cast(key.data()); + attrs.push_back(attr); + + attr.id = SAI_OCS_PORT_ATTR_OVERRIDE_STATE; + attr.value.u32 = (uint32_t)SAI_OCS_PORT_OVERRIDE_STATE_NORMAL; + attrs.push_back(attr); + + attr_data_list.push_back(attrs); + attr_count_list.push_back(static_cast(attr_data_list.back().size())); + attr_ptr_list.push_back(attr_data_list.back().data()); + } + + auto cnt = keys.size(); + vector oid_list(cnt, SAI_NULL_OBJECT_ID); + vector status_list(cnt, SAI_STATUS_SUCCESS); + + auto status = sai_ocs_api->create_ocs_ports( + gSwitchId, (int32_t)cnt, attr_count_list.data(), attr_ptr_list.data(), + SAI_BULK_OP_ERROR_MODE_STOP_ON_ERROR, + oid_list.data(), status_list.data() + ); + + if (SAI_STATUS_SUCCESS == status) + { + for (size_t i = 0; i < cnt; ++i) + { + _port_key_to_saiobjid_map[keys[i]] = oid_list[i]; + } + } +``` + +## 8.2. Usage Example: Change OCS Port Configuration +Following is example about how to set override status of OCS port 1A to force-blocked. +```cpp +string port_id = "1A"; +auto it = _port_key_to_saiobjid_map.find(port_id); + +if (it != _port_key_to_saiobjid_map.end()) +{ + sai_attribute_t attr; + attr.id = SAI_OCS_PORT_ATTR_OVERRIDE_STATE; + attr.value.u32 = (uint32_t)SAI_OCS_PORT_OVERRIDE_STATE_FORCE_BLOCKED; + + auto status = sai_ocs_api->set_ocs_port_attribute(it->second, &attr); +} +``` +## 8.3. Usage Example: Get an OCS Port Operational Status +Following is an example of how to get operational status of OCS port 1A +```cpp +string port = "1A"; +auto it = _port_key_to_saiobjid_map.find(port); + +if (_port_key_to_saiobjid_map.end() != it) +{ + sai_attribute_t attr; + sai_status_t status; + attr.id = SAI_OCS_PORT_ATTR_OPER_STATUS; + + status = sai_ocs_api->get_ocs_port_attribute(it->second, 1, &attr); +} +``` +## 8.4. Usage Example: Get All OCS Port Operational Status +Following is an example of how to get operational status of all OCS ports +```cpp +vector oid_list; +vector port_keys; + +for (auto& p : _port_key_to_saiobjid_map) +{ + port_keys.push_back(p.first); + oid_list.push_back(p.second); +} + +auto portNum = oid_list.size(); + +std::vector attrs(portNum); +vector attr_count_list(portNum, 1); +vector attr_ptr_list(portNum); + +for (size_t i = 0; i < (uint32_t)portNum; i++) +{ + attrs[i].id = SAI_OCS_PORT_ATTR_OPER_STATUS; + attr_ptr_list[i] = &attrs[i]; +} + +vector status_list(portNum, SAI_STATUS_SUCCESS); + +auto status = sai_ocs_api->get_ocs_ports_attribute( + (int32_t)portNum, + oid_list.data(), + attr_count_list.data(), + attr_ptr_list.data(), + SAI_BULK_OP_ERROR_MODE_STOP_ON_ERROR, + status_list.data() +); +``` +## 8.5. Usage Example: Create OCS Cross-Connects +The following example demonstrates how to create multiple OCS cross-connects: 1A-1B, 2A-2B, 3A-2B and 4A-4B. Bulk creation API is used to have fast OCS cross-connect switching performance. Note: the returned SAI object IDs need to be buffered and they will be used for remove operations. +```cpp + vector> attr_data_list; + vector attr_count_list; + vector attr_ptr_list; + + vector > conns = {make_pair("1A", "1B"), make_pair("2A", "2B"), make_pair("3A", "3B"), make_pair("4A", "4B")}; + + for (auto& conn : conns) + { + sai_attribute_t attr; + vector attrs; + + auto it_a = _port_key_to_saiobjid_map.find(conn.first); + + if (it_a == _port_key_to_saiobjid_map.end()) + { + continue; + } + + attr.id = SAI_OCS_CROSS_CONNECT_ATTR_A_SIDE_PORT_ID; + attr.value.oid = it_a->second; + attrs.push_back(attr); + + auto it_b = _port_key_to_saiobjid_map.find(conn.second); + + if (it_b == _port_key_to_saiobjid_map.end()) + { + continue; + } + + attr.id = SAI_OCS_CROSS_CONNECT_ATTR_B_SIDE_PORT_ID; + attr.value.oid = it_b->second; + attrs.push_back(attr); + + attr_data_list.push_back(attrs); + attr_count_list.push_back(static_cast(attr_data_list.back().size())); + attr_ptr_list.push_back(attr_data_list.back().data()); + } + + auto conn_num = attr_data_list.size(); + vector oid_list(conn_num, SAI_NULL_OBJECT_ID); + vector status_list(conn_num, SAI_STATUS_SUCCESS); + + auto status = sai_ocs_api->create_ocs_cross_connects( + gSwitchId, (int32_t)conn_num, attr_count_list.data(), attr_ptr_list.data(), + SAI_BULK_OP_ERROR_MODE_STOP_ON_ERROR, + oid_list.data(), status_list.data() + ); +``` +## 8.6. Usage Example: Delete OCS Cross-Connects +The following example demonstrates how to remove multiple OCS cross-connects. +```cpp +vector keys = {"1A-1B", "2A-2B"}; +vector oid_list; +vector status_list(keys.size(), SAI_STATUS_SUCCESS); + +for (auto& key : keys) +{ + auto oid = getFromCrsKeyToSaiObjIdMap(key); + + if (0 == oid) continue; + + oid_list.emplace_back(oid); +} + +auto status = sai_ocs_api->remove_ocs_cross_connects((int32_t)oid_list.size(), + oid_list.data(), + SAI_BULK_OP_ERROR_MODE_STOP_ON_ERROR, + status_list.data()); +``` \ No newline at end of file diff --git a/doc/OCS/images/port_status.png b/doc/OCS/images/port_status.png new file mode 100755 index 0000000000000000000000000000000000000000..366816c596e6a93c47b6fa5dde917daef6addc1d GIT binary patch literal 28207 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 aD)~G_x!Yzrfq3NYk+g(@c)8dozyAVA_l#u# literal 0 HcmV?d00001 diff --git a/experimental/saiexperimentalocs.h b/experimental/saiexperimentalocs.h new file mode 100644 index 000000000..f1afa5b56 --- /dev/null +++ b/experimental/saiexperimentalocs.h @@ -0,0 +1,598 @@ +/** + * Copyright (c) 2014 Microsoft Open Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT + * LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS + * FOR A PARTICULAR PURPOSE, MERCHANTABILITY OR NON-INFRINGEMENT. + * + * See the Apache Version 2.0 License for specific language governing + * permissions and limitations under the License. + * + * Microsoft would like to thank the following companies for their review and + * assistance with these files: Intel Corporation, Mellanox Technologies Ltd, + * Dell Products, L.P., Facebook, Inc., Marvell International Ltd. + * + * @file saiexperimentalocs.h + * + * @brief This module defines SAI OCS + * + * @warning This module is a SAI experimental module + */ + +#if !defined (__SAIEXPERIMENTALOCS_H_) +#define __SAIEXPERIMENTALOCS_H_ + +#include + +/** + * @defgroup SAIEXPERIMENTALOCS SAI - Experimental: OCS specific API definitions + * + * @{ + */ + +/** + * @brief List of OCS cross connect attributes + */ +typedef enum _sai_ocs_cross_connect_attr_t +{ + /** + * @brief Start of attributes + */ + SAI_OCS_CROSS_CONNECT_ATTR_START, + + /** + * @brief A side port + * + * @type sai_object_id_t + * @flags MANDATORY_ON_CREATE | CREATE_ONLY + * @objects SAI_OBJECT_TYPE_OCS_PORT + */ + SAI_OCS_CROSS_CONNECT_ATTR_A_SIDE_PORT_ID = SAI_OCS_CROSS_CONNECT_ATTR_START, + + /** + * @brief B side port + * + * @type sai_object_id_t + * @flags MANDATORY_ON_CREATE | CREATE_ONLY + * @objects SAI_OBJECT_TYPE_OCS_PORT + */ + SAI_OCS_CROSS_CONNECT_ATTR_B_SIDE_PORT_ID, + + /** + * @brief End of attributes + */ + SAI_OCS_CROSS_CONNECT_ATTR_END, + + /** Custom range base value */ + SAI_OCS_CROSS_CONNECT_ATTR_CUSTOM_RANGE_START = 0x10000000, + + /** End of custom range base */ + SAI_OCS_CROSS_CONNECT_ATTR_CUSTOM_RANGE_END, + +} sai_ocs_cross_connect_attr_t; + +/** + * @brief OCS cross-connect counter IDs in sai_get_ocs_cross_connect_stats() call + */ +typedef enum _sai_ocs_cross_connect_stat_t +{ + /** OCS cross-connect stat unknown, it is used to make build pass, more stats can be added later. */ + SAI_OCS_CROSS_CONNECT_STAT_UNKNOWN, +} sai_ocs_cross_connect_stat_t; + +/** + * @brief Create cross connect entry. + * + * @param[out] ocs_cross_connect_id OCS cross connect id + * @param[in] switch_id Switch id + * @param[in] attr_count Number of attributes + * @param[in] attr_list Array of attributes + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_create_ocs_cross_connect_fn)( + _Out_ sai_object_id_t *ocs_cross_connect_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + +/** + * @brief Remove cross connect entry + * + * @param[in] ocs_cross_connect_id OCS cross connect Id + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_remove_ocs_cross_connect_fn)( + _In_ sai_object_id_t ocs_cross_connect_id); + +/** + * @brief Set cross connect entry attribute + * + * @param[in] ocs_cross_connect_id OCS cross connect id + * @param[in] attr Attribute + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_set_ocs_cross_connect_attribute_fn)( + _In_ sai_object_id_t ocs_cross_connect_id, + _In_ const sai_attribute_t *attr); + +/** + * @brief Get OCS cross connect entry attribute + * + * @param[in] ocs_cross_connect_id OCS cross connect id + * @param[in] attr_count Number of attributes + * @param[inout] attr_list Array of attributes + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_get_ocs_cross_connect_attribute_fn)( + _In_ sai_object_id_t ocs_cross_connect_id, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list); + +/** + * @brief Get OCS cross-connect statistics counters. Deprecated for backward compatibility. + * + * @param[in] ocs_cross_connect_id OCS cross-connect id + * @param[in] number_of_counters Number of counters in the array + * @param[in] counter_ids Specifies the array of counter ids + * @param[out] counters Array of resulting counter values. + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_get_ocs_cross_connect_stats_fn)( + _In_ sai_object_id_t ocs_cross_connect_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _Out_ uint64_t *counters); + +/** + * @brief Get OCS cross-connect statistics counters extended. + * + * @param[in] ocs_cross_connect_id OCS cross-connect id + * @param[in] number_of_counters Number of counters in the array + * @param[in] counter_ids Specifies the array of counter ids + * @param[in] mode Statistics mode + * @param[out] counters Array of resulting counter values. + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_get_ocs_cross_connect_stats_ext_fn)( + _In_ sai_object_id_t ocs_cross_connect_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _In_ sai_stats_mode_t mode, + _Out_ uint64_t *counters); + +/** + * @brief Clear OCS cross-connect statistics counters. + * + * @param[in] ocs_cross_connect_id OCS cross-connect id + * @param[in] number_of_counters Number of counters in the array + * @param[in] counter_ids Specifies the array of counter ids + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_clear_ocs_cross_connect_stats_fn)( + _In_ sai_object_id_t ocs_cross_connect_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids); + +/** + * @brief Attribute data for OCS port override state parameter + */ +typedef enum _sai_ocs_port_override_state_t +{ + /** + * @brief Default, port status to be determined by presence of cross-connect + */ + SAI_OCS_PORT_OVERRIDE_STATE_NORMAL, + + /** + * @brief Regardless of cross-connect presence state, blocks the port + */ + SAI_OCS_PORT_OVERRIDE_STATE_FORCE_BLOCKED + +} sai_ocs_port_override_state_t; + +/** + * @brief Attribute data for OCS port status parameter + */ +typedef enum _sai_ocs_port_status_t +{ + /** + * @brief Default status for any port which is with no configuration + */ + SAI_OCS_PORT_STATUS_BLOCKED, + + /** + * @brief Status when device is performing the switching. + */ + SAI_OCS_PORT_STATUS_TUNING, + + /** + * @brief Status when the device has finished switching. + */ + SAI_OCS_PORT_STATUS_TUNED, + + /** + * @brief This is not a configuration or permanent failure flag. It is meant as a status in the port tuning state machine, for example, when software fails to apply a beam steering state to the OCS module due to some communication issue. It is up to the vendor to determine what constitutes a failure in port tuning. Note that the "failed" status is not an all-inclusive status but only used at times that the software knows of a certain failure in tuning. + */ + SAI_OCS_PORT_STATUS_FAILED +} sai_ocs_port_status_t; + +/** + * @brief List of OCS port attributes + */ +typedef enum _sai_ocs_port_attr_t +{ + /** + * @brief Start of device port attributes + */ + SAI_OCS_PORT_ATTR_START, + + /** + * @brief Unique identifier for a port + * + * @type sai_u8_list_t + * @flags MANDATORY_ON_CREATE | CREATE_ONLY + */ + SAI_OCS_PORT_ATTR_NAME = SAI_OCS_PORT_ATTR_START, + + /** + * @brief Overrides the status imposed by the programmed cross-connects + * + * @type sai_ocs_port_override_state_t + * @flags CREATE_AND_SET + * @default SAI_OCS_PORT_OVERRIDE_STATE_NORMAL + */ + SAI_OCS_PORT_ATTR_OVERRIDE_STATE, + + /** + * @brief Operational status of the port + * + * @type sai_ocs_port_status_t + * @flags READ_ONLY + */ + SAI_OCS_PORT_ATTR_OPER_STATUS, + + /** + * @brief Mapping between face plate port and OCS physical element + * + * @type sai_u8_list_t + * @flags READ_ONLY + */ + SAI_OCS_PORT_ATTR_PHYSICAL_MAPPING, + + /** + * @brief End of attributes + */ + SAI_OCS_PORT_ATTR_END, + + /** Custom range base value */ + SAI_OCS_PORT_ATTR_CUSTOM_RANGE_START = 0x10000000, + + /** End of custom range base */ + SAI_OCS_PORT_ATTR_CUSTOM_RANGE_END, +} sai_ocs_port_attr_t; + +/** + * @brief OCS port counter IDs in sai_get_ocs_port_stats() call + */ +typedef enum _sai_ocs_port_stat_t +{ + /** OCS port stat unknown, it is used to make build pass, more stats can be added later. */ + SAI_OCS_PORT_STAT_UNKNOWN, +} sai_ocs_port_stat_t; + +/** + * @brief Defines the operational status of the OCS port + */ +typedef struct _sai_ocs_port_status_notification_t +{ + /** + * @brief OCS Port id. + * + * @objects SAI_OBJECT_TYPE_OCS_PORT + */ + sai_object_id_t port_id; + + /** OCS port operational status */ + sai_ocs_port_status_t port_state; +} sai_ocs_port_status_notification_t; + +/** + * @brief Create port entry. + * + * @param[out] ocs_port_id OCS port id + * @param[in] switch_id Switch id + * @param[in] attr_count Number of attributes + * @param[in] attr_list Array of attributes + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_create_ocs_port_fn)( + _Out_ sai_object_id_t *ocs_port_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + +/** + * @brief Remove port entry + * + * @param[in] ocs_port_id OCS port Id + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_remove_ocs_port_fn)( + _In_ sai_object_id_t ocs_port_id); + +/** + * @brief Set port entry attribute + * + * @param[in] ocs_port_id OCS port id + * @param[in] attr Attribute + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_set_ocs_port_attribute_fn)( + _In_ sai_object_id_t ocs_port_id, + _In_ const sai_attribute_t *attr); + +/** + * @brief Get OCS port entry attribute + * + * @param[in] ocs_port_id OCS port id + * @param[in] attr_count Number of attributes + * @param[inout] attr_list Array of attributes + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_get_ocs_port_attribute_fn)( + _In_ sai_object_id_t ocs_port_id, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list); + +/** + * @brief Get OCS port statistics counters. Deprecated for backward compatibility. + * + * @param[in] ocs_port_id OCS port id + * @param[in] number_of_counters Number of counters in the array + * @param[in] counter_ids Specifies the array of counter ids + * @param[out] counters Array of resulting counter values. + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_get_ocs_port_stats_fn)( + _In_ sai_object_id_t ocs_port_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _Out_ uint64_t *counters); + +/** + * @brief Get OCS port statistics counters extended. + * + * @param[in] ocs_port_id OCS port id + * @param[in] number_of_counters Number of counters in the array + * @param[in] counter_ids Specifies the array of counter ids + * @param[in] mode Statistics mode + * @param[out] counters Array of resulting counter values. + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_get_ocs_port_stats_ext_fn)( + _In_ sai_object_id_t ocs_port_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _In_ sai_stats_mode_t mode, + _Out_ uint64_t *counters); + +/** + * @brief Clear OCS port statistics counters. + * + * @param[in] ocs_port_id OCS port id + * @param[in] number_of_counters Number of counters in the array + * @param[in] counter_ids Specifies the array of counter ids + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_clear_ocs_port_stats_fn)( + _In_ sai_object_id_t ocs_port_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids); + +/** + * @brief OCS port state change notification + * + * Passed as a parameter into sai_initialize_switch() + * + * @count data[count] + * + * @param[in] count Number of notifications + * @param[in] data Array of OCS port operational status + */ +typedef void (*sai_ocs_port_state_change_notification_fn)( + _In_ uint32_t count, + _In_ const sai_ocs_port_status_notification_t *data); + +/** + * @brief List of OCS cross connect factory data attributes. Inventory data for all possible cross-connects, + * factory insertion loss measurements + */ +typedef enum _sai_ocs_cross_connect_factory_data_attr_t +{ + /** + * @brief Start of attributes + */ + SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_START, + + /** + * @brief Name of A side port in a cross connection + * + * @type sai_u8_list_t + * @flags READ_ONLY + */ + SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_A_SIDE_PORT_NAME = SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_START, + + /** + * @brief Name of B side port in a cross connection. + * + * @type sai_u8_list_t + * @flags READ_ONLY + */ + SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_B_SIDE_PORT_NAME, + + /** + * @brief Center frequency at which insertion loss is measured, e.g., 229.1 (O-band) 193.5, use int32 for representation of decimal value + * + * List of center frequency values. Use int32 for representation of decimal value with 3 fraction digits + * + * @type sai_s32_list_t + * @flags READ_ONLY + */ + SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_FREQUENCY_THZ, + + /** + * @brief Internal temperature sensor that correlates with the ambient temperature at which + * the insertion loss is measured in Celsius, e.g., 25.00 + * + * List of temperatures. Use int32 for representation of decimal value with 2 fraction digits + * + * @type sai_s32_list_t + * @flags READ_ONLY + */ + SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_MEASURED_TEMPERATURE, + + /** + * @brief Factory insertion loss in dB, e.g. 1.23 + * + * List of insertion loss values. Use int32 for representation of decimal value with 2 fraction digits + * + * @type sai_s32_list_t + * @flags READ_ONLY + */ + SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_INSERTION_LOSS_DB, + + /** + * @brief Factory insertion loss in dB, e.g. 1.23 + * + * List of insertion loss accuracy values. Use int32 for representation of decimal value with 2 fraction digits + * + * @type sai_s32_list_t + * @flags READ_ONLY + */ + SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_INSERTION_LOSS_ACCURACY_DB, + + /** + * @brief End of attributes + */ + SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_END, + + /** Custom range base value */ + SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_CUSTOM_RANGE_START = 0x10000000, + + /** End of custom range base */ + SAI_OCS_CROSS_CONNECT_FACTORY_DATA_ATTR_CUSTOM_RANGE_END, +} sai_ocs_cross_connect_factory_data_attr_t; + +/** + * @brief Create OCS cross connect factory data entry. + * + * @param[out] ocs_cross_connect_factory_data_id OCS cross connect factory data id + * @param[in] switch_id Switch id + * @param[in] attr_count Number of attributes + * @param[in] attr_list Array of attributes + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_create_ocs_cross_connect_factory_data_fn)( + _Out_ sai_object_id_t *ocs_cross_connect_factory_data_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + +/** + * @brief Remove OCS cross connect factory data entry + * + * @param[in] ocs_cross_connect_factory_data_id OCS cross connect Id + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_remove_ocs_cross_connect_factory_data_fn)( + _In_ sai_object_id_t ocs_cross_connect_factory_data_id); + +/** + * @brief Set OCS cross connect factory data entry attribute + * + * @param[in] ocs_cross_connect_factory_data_id OCS cross connect id + * @param[in] attr Attribute + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_set_ocs_cross_connect_factory_data_attribute_fn)( + _In_ sai_object_id_t ocs_cross_connect_factory_data_id, + _In_ const sai_attribute_t *attr); + +/** + * @brief Get OCS cross connect factory data attribute + * + * @param[in] ocs_cross_connect_factory_data_id OCS cross connect id + * @param[in] attr_count Number of attributes + * @param[inout] attr_list Array of attributes + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_get_ocs_cross_connect_factory_data_attribute_fn)( + _In_ sai_object_id_t ocs_cross_connect_factory_data_id, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list); + +/** + * @brief OCS methods table retrieved with sai_api_query() + */ +typedef struct _sai_ocs_api_t +{ + sai_create_ocs_port_fn create_ocs_port; + sai_remove_ocs_port_fn remove_ocs_port; + sai_set_ocs_port_attribute_fn set_ocs_port_attribute; + sai_get_ocs_port_attribute_fn get_ocs_port_attribute; + sai_bulk_object_create_fn create_ocs_ports; + sai_bulk_object_remove_fn remove_ocs_ports; + sai_bulk_object_set_attribute_fn set_ocs_ports_attribute; + sai_bulk_object_get_attribute_fn get_ocs_ports_attribute; + sai_get_ocs_port_stats_fn get_ocs_port_stats; + sai_get_ocs_port_stats_ext_fn get_ocs_port_stats_ext; + sai_clear_ocs_port_stats_fn clear_ocs_port_stats; + sai_create_ocs_cross_connect_fn create_ocs_cross_connect; + sai_remove_ocs_cross_connect_fn remove_ocs_cross_connect; + sai_set_ocs_cross_connect_attribute_fn set_ocs_cross_connect_attribute; + sai_get_ocs_cross_connect_attribute_fn get_ocs_cross_connect_attribute; + sai_get_ocs_cross_connect_stats_fn get_ocs_cross_connect_stats; + sai_get_ocs_cross_connect_stats_ext_fn get_ocs_cross_connect_stats_ext; + sai_clear_ocs_cross_connect_stats_fn clear_ocs_cross_connect_stats; + sai_bulk_object_create_fn create_ocs_cross_connects; + sai_bulk_object_remove_fn remove_ocs_cross_connects; + sai_bulk_object_set_attribute_fn set_ocs_cross_connects_attribute; + sai_bulk_object_get_attribute_fn get_ocs_cross_connects_attribute; + sai_create_ocs_cross_connect_factory_data_fn create_ocs_cross_connect_factory_data; + sai_remove_ocs_cross_connect_factory_data_fn remove_ocs_cross_connect_factory_data; + sai_set_ocs_cross_connect_factory_data_attribute_fn set_ocs_cross_connect_factory_data_attribute; + sai_get_ocs_cross_connect_factory_data_attribute_fn get_ocs_cross_connect_factory_data_attribute; + sai_bulk_object_create_fn create_ocs_cross_connect_factory_datas; + sai_bulk_object_remove_fn remove_ocs_cross_connect_factory_datas; + sai_bulk_object_set_attribute_fn set_ocs_cross_connect_factory_datas_attribute; + sai_bulk_object_get_attribute_fn get_ocs_cross_connect_factory_datas_attribute; + +} sai_ocs_api_t; + +/** + * @} + */ +#endif /** __SAIEXPERIMENTALOCS_H_ */ diff --git a/experimental/saiextensions.h b/experimental/saiextensions.h index 7d4e5e7cc..58187d0e0 100644 --- a/experimental/saiextensions.h +++ b/experimental/saiextensions.h @@ -51,6 +51,7 @@ #include "saiexperimentaldashdirectionlookup.h" #include "saiexperimentaldashacl.h" #include "saiexperimentalbmtor.h" +#include "saiexperimentalocs.h" /* OTN Extensions */ #include "saiexperimentalotnattenuator.h" @@ -106,6 +107,8 @@ typedef enum _sai_api_extensions_t SAI_API_OTN_OA, + SAI_API_OCS, + /* Add new experimental APIs above this line */ SAI_API_EXTENSIONS_RANGE_END diff --git a/experimental/saiswitchextensions.h b/experimental/saiswitchextensions.h index 01c535963..5dfe6d9cb 100644 --- a/experimental/saiswitchextensions.h +++ b/experimental/saiswitchextensions.h @@ -262,6 +262,17 @@ typedef enum _sai_switch_attr_extensions_t */ SAI_SWITCH_ATTR_FLOW_BULK_GET_SESSION_EVENT_NOTIFY, + /** + * @brief Set Switch OCS port state change event notification callback function passed to the adapter. + * + * Use sai_ocs_port_state_change_notification_fn as notification function. + * + * @type sai_pointer_t sai_ocs_port_state_change_notification_fn + * @flags CREATE_AND_SET + * @default NULL + */ + SAI_SWITCH_ATTR_OCS_PORT_STATE_CHANGE_NOTIFY, + SAI_SWITCH_ATTR_EXTENSIONS_RANGE_END } sai_switch_attr_extensions_t; diff --git a/experimental/saitypesextensions.h b/experimental/saitypesextensions.h index 1e0af7002..2a54fcc03 100644 --- a/experimental/saitypesextensions.h +++ b/experimental/saitypesextensions.h @@ -107,6 +107,12 @@ typedef enum _sai_object_type_extensions_t SAI_OBJECT_TYPE_OTN_OA, + SAI_OBJECT_TYPE_OCS_CROSS_CONNECT, + + SAI_OBJECT_TYPE_OCS_PORT, + + SAI_OBJECT_TYPE_OCS_CROSS_CONNECT_FACTORY_DATA, + /* Add new experimental object types above this line */ SAI_OBJECT_TYPE_EXTENSIONS_RANGE_END diff --git a/inc/saiobject.h b/inc/saiobject.h index 84dd67018..875669f33 100644 --- a/inc/saiobject.h +++ b/inc/saiobject.h @@ -55,6 +55,7 @@ #include #include #include +#include /** * @defgroup SAIOBJECT SAI - Object API definitions. diff --git a/meta/acronyms.txt b/meta/acronyms.txt index 8f4dee181..b4da06606 100644 --- a/meta/acronyms.txt +++ b/meta/acronyms.txt @@ -120,6 +120,7 @@ NTPSERVER - Network Time Protocol Server NVGRE - Network Virtualization using Generic Routing Encapsulation OA - Optical Amplifier OAM - Operations Administration and Maintenance +OCS - Optical Circuit Switch ORL - Optical Return Loss OTN - Optical Transport Network OUI - Organizationally Unique Identifier From 8511faf1b738a6f95081c4839f682721e90b71ec Mon Sep 17 00:00:00 2001 From: Ron He Date: Mon, 3 Aug 2026 13:24:17 -0700 Subject: [PATCH 11/11] Add label attribute for ACL entry (#2322) Summary: This is similar to #1158, #1407, #1430. Those PRs added label attribute for LAG/virtual router, counter, ACL Counter. This PR adds similar label attribute for ACL entries. Adding a label attribute that can be used to uniquely identify ACL entry object during warmboot. This attribute is considered as user data attached to the object. Signed-off-by: Shrikrishna (Shri) Khare Signed-off-by: Jasmeet Bagga Signed-off-by: Ron He Co-authored-by: Ron He --- inc/saiacl.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/inc/saiacl.h b/inc/saiacl.h index fa6b60904..77fa2b138 100644 --- a/inc/saiacl.h +++ b/inc/saiacl.h @@ -1850,6 +1850,15 @@ typedef enum _sai_acl_entry_attr_t */ SAI_ACL_ENTRY_ATTR_ADMIN_STATE, + /** + * @brief Attribute used to uniquely identify ACL entry. + * + * @type sai_s8_list_t + * @flags CREATE_AND_SET + * @default empty + */ + SAI_ACL_ENTRY_ATTR_LABEL, + /* * Match fields [sai_acl_field_data_t] * - Mandatory to pass at least one field during ACL Rule creation.