From 3af7fdd1cbf543ccb0b78ad579fcf683305538ef Mon Sep 17 00:00:00 2001 From: JaiOCP Date: Tue, 21 Apr 2026 13:18:18 -0700 Subject: [PATCH 1/4] SAI API Performance Monitoring Signed-off-by: JaiOCP --- doc/perfmon/SAI-perfmon-Spec.md | 243 ++++++++++++++++++++++++++++++++ inc/sai.h | 2 + inc/saiperfmon.h | 190 +++++++++++++++++++++++++ inc/saiswitch.h | 10 ++ inc/saitypes.h | 1 + 5 files changed, 446 insertions(+) create mode 100755 doc/perfmon/SAI-perfmon-Spec.md create mode 100644 inc/saiperfmon.h diff --git a/doc/perfmon/SAI-perfmon-Spec.md b/doc/perfmon/SAI-perfmon-Spec.md new file mode 100755 index 000000000..22ad40a0b --- /dev/null +++ b/doc/perfmon/SAI-perfmon-Spec.md @@ -0,0 +1,243 @@ +# Performance Monitoring SAI Specification +------------------------------------------------------------------------------- + Title | SAI support for Performance Monitoring +:-------------|:----------------------------------------------------------------- + Authors | Jai Kumar, Broadcom Inc + Status | In review + Type | Standards track + Created | 03/18/2026: Initial Draft + SAI-Version | 1.19 +------------------------------------------------------------------------------- + + +## 1.0 Introduction +As network fabric scale increases and data centers require regional spine connectivity, the number of downlinks for cluster connectivity is growing. This leads to more LAGs, more prefixes, and larger ECMP. This is also true for large scale up and scale across fabrics for AI/ML. + +This increasing scale mandates that SAI be scalable, reliable, and high-performance. This specification addresses the performance component of SAI by introducing a new set of metrics to accurately measure the performance of various components within the SAI layer and below, such as SDK and hardware updates. + +Using these metrics, deployments can isolate components impacting performance and focus on their optimization. + + + +## 2.0 Terms and Acronyms + +| Term| Description | +|:---|:---| +| perfmon | Performance Metrics | + +## 3.0 Overview +The SAI infrastructure exposes a set of APIs as a standard interface to the upper layer. + +These APIs are synchronous and blocking, making the completion time of any given API a critical performance measure. Note that application-specific callbacks are not addressed by this specification. + +``` +/** + * @brief SAI common API type + */ +typedef enum _sai_common_api_t +{ + SAI_COMMON_API_CREATE = 0, + SAI_COMMON_API_REMOVE = 1, + SAI_COMMON_API_SET = 2, + SAI_COMMON_API_GET = 3, + SAI_COMMON_API_BULK_CREATE = 4, + SAI_COMMON_API_BULK_REMOVE = 5, + SAI_COMMON_API_BULK_SET = 6, + SAI_COMMON_API_BULK_GET = 7, + SAI_COMMON_API_MAX = 8, +} sai_common_api_t; + +``` + +This specification proposes API performance measures for the following metrics +1. Average Latency +2. Instantaneous Latency +3. Maximum Latency + +### 3.1 Average, Instantaneous, and Maximum Latency +API completion time consists of the time spent in the SAI adapter and the SDK, including hardware update or query time. Time measured is irrespetcive of the status of the API call i.e. if the API call completes with error status, adapter will still account the measured latency during the time interval of the metrics computation. NOS tracks the return status of API calls and can account for errors as needed. Discounting latency for specific error statuses would result in inconsistent measurements, requiring metric subscribers to implement manual workarounds for those cases. + +These metrics can be used to: +- Improve SAI adapter and SDK implementations +- Provide a baseline for comparing different hardware +- Instantaneous value: Provides [time, n], where n > 1 represents the number of objects in a bulk API, or n = 1 represents the last observed latency for a single object +- Maximum: The highest value observed across the last n invocations +- Average: The average value over the last n invocations. + + +## 4.0 SAI Specification +New perfmon object is introduced. Each perfmon object specifies the object of interest, set of APIs and metrics to be measured for each API. + + +Each perfmon object created has a binding to the switch object. + +### 4.2 Perfmon Object +New perfmon object is introduced specifying API and metrics of interest. + +#### 4.3.1 Metrics +Each API can be measure for a specific performance metrics as specified in sai_perfmon_metrics_t + +``` +/** + * @brief Performance Monitoring Metrics + */ +typedef enum _sai_perfmon_metrics_t +{ + /** + * @brief None + */ + SAI_PERFMON_METRICS_NONE, + + /** + * @brief Maximum latency observed + */ + SAI_PERFMON_METRICS_MAX_LATENCY, + + /** + * @brief Average latency observed + */ + SAI_PERFMON_METRICS_AVERAGE_LATENCY, + + /** + * @brief Instantaneous latency observed + */ + SAI_PERFMON_METRICS_INST_LATENCY, + +} sai_perfmon_metrics_t; + +``` + +#### 4.3.2 Perfmon Object Attributes +Type of API to be monitored for performance and its associated attributes are specified in the perfmon object attributes + +``` +/** + * @brief Performance Monitoring Attributes + */ +typedef enum _sai_perfmon_attr_t +{ + /** + * @brief Start of Attributes + */ + SAI_PERFMON_ATTR_START, + + /** + * @brief Object to be monitored + * + * @type sai_object_type_t + * @flags MANDATORY_ON_CREATE | CREATE_ONLY + */ + SAI_PERFMON_ATTR_OBJECT_TYPE = SAI_PERFMON_ATTR_START, + + /** + * @brief API to be monitored + * + * @type sai_common_api_t + * @flags CREATE_AND_SET + */ + SAI_PERFMON_ATTR_COMMON_API, + + /** + * @brief Performance metrics to be collected + * + * @type sai_perfmon_metrics_t + * @flags CREATE_AND_SET + * @default SAI_PERFMON_METRICS_NONE + */ + SAI_PERFMON_ATTR_PERFMON_METRICS, + + /** + * @brief Performance data as collected. This is clear on read. + * Performance data is computed once enabled and is cleared once read. + * + * @type sai_uint64_t + * @flags READ_ONLY + */ + SAI_PERFMON_ATTR_PERFDATA, + + /** + * @brief End of Performance Monitoring attributes + */ + SAI_PERFMON_ATTR_END, + + /** Custom range base value */ + SAI_PERFMON_ATTR_CUSTOM_RANGE_START = 0x10000000, + + /** End of custom range base */ + SAI_PERFMON_ATTR_CUSTOM_RANGE_END + +} sai_perfmon_attr_t; + +``` + +#### 4.3.3 Perfmon Object Switch Binding +List of perfmon objects can be bound to the switch object. This binding can be done as a SET operation when perfmon object is created. + +``` + /** + * @brief Performance Monitoring enabled on the switch + * + * @type sai_object_list_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_PERFMO$ + * @default empty + */ + SAI_SWITCH_ATTR_PERFMON_LIST, +``` + + +## 5.0 Sample Workflow + +This section talks about enabling performance monitoring for a given API and a metrics. + +### 5.1 Create perfmon object +- Each perfmon object supports a single API and a single set of metrics. To monitor additional metrics for the same API or to monitor a different API, a new perfmon object must be created. +- Monitoring in the SAI adapter will only begin once the perfmon object is bound to the switch object. + +``` +/* + * Configure CSIG Compact Tag for ABW signal processing and time interval of 256 micro seconds + */ + +// Specify the Object of intererst +sai_attr_list[0].id = SAI_PERFMON_ATTR_OBJECT_TYPE; +sai_attr_list[0].value.s32 = SAI_OBJECT_TYPE_ROUTE_ENTRY; + +// Specify the API of interest +sai_attr_list[1].id = SAI_PERFMON_ATTR_COMMON_API; +sai_attr_list[1].value.s32 = SAI_COMMON_API_BULK_SET; + +// Configure metrics to be measured +sai_attr_list[2].id = SAI_PERFMON_ATTR_PERFMON_METRICS; +sai_attr_list[2].value.s32 = SAI_PERFMON_METRICS_AVERAGE_LATENCY; + +// Configure Time Interval in msec +sai_attr_list[3].id = SAI_PERFMON_ATTR_METRICS_TIME_INTERVAL; +sai_attr_list[3].value.u32 = 2048; + + +// Create perfmon object +attr_count = 4; +create_perfmon( + &sai_perfmon_object, + switch_id, + attr_count, + sai_attr_list); +``` + +### 5.2 Read perfmon Metrics + +Read the perfmon attribute for getting the API related metrics. + +``` +// Specify the read attribute +sai_attr_list[1].id = SAI_PERFMON_ATTR_PERFDATA; + +// Read perfmon metrics +attr_count = 1; +get_perfmon_attribute( + sai_perfmon_object, + attr_count, + sai_attr_list); +... + diff --git a/inc/sai.h b/inc/sai.h index d8f90c44b..bfeb264fa 100644 --- a/inc/sai.h +++ b/inc/sai.h @@ -48,6 +48,7 @@ #include "sainexthopgroup.h" #include "sainexthop.h" #include "saiobject.h" +#include "saiperfmon.h" #include "saipolicer.h" #include "saiport.h" #include "saiqosmap.h" @@ -155,6 +156,7 @@ typedef enum _sai_api_t SAI_API_PREFIX_COMPRESSION = 53, /**< sai_prefix_compression_api_t */ SAI_API_SYNCE = 54, /**< sai_synce_api_t */ SAI_API_VIRTUAL_CHANNEL = 55, /**< sai_virtual_channel_api_t */ + SAI_API_PERFMON = 56, /**< sai_perfmon_api_t */ SAI_API_MAX, /**< total number of APIs */ /** diff --git a/inc/saiperfmon.h b/inc/saiperfmon.h new file mode 100644 index 000000000..a9c846bc5 --- /dev/null +++ b/inc/saiperfmon.h @@ -0,0 +1,190 @@ +/** + * 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 saiperfmon.h + * + * @brief This module defines SAI Performance Monitoring spec + */ + +#if !defined (__SAIPERFMON_H_) +#define __SAIPERFMON_H_ + +#include + +/** + * @defgroup SAIPERFMON SAI - Performance Monitoring specific API definitions + * + * @{ + */ + +/** + * @brief Performance Monitoring Metrics + */ +typedef enum _sai_perfmon_metrics_t +{ + /** + * @brief None + */ + SAI_PERFMON_METRICS_NONE, + + /** + * @brief Maximum latency observed + */ + SAI_PERFMON_METRICS_MAX_LATENCY, + + /** + * @brief Average latency observed + */ + SAI_PERFMON_METRICS_AVERAGE_LATENCY, + + /** + * @brief Instantaneous latency observed + */ + SAI_PERFMON_METRICS_INST_LATENCY, + +} sai_perfmon_metrics_t; + +/** + * @brief Performance Monitoring Attributes + */ +typedef enum _sai_perfmon_attr_t +{ + /** + * @brief Start of Attributes + */ + SAI_PERFMON_ATTR_START, + + /** + * @brief Object to be monitored + * + * @type sai_object_type_t + * @flags MANDATORY_ON_CREATE | CREATE_ONLY + */ + SAI_PERFMON_ATTR_OBJECT_TYPE = SAI_PERFMON_ATTR_START, + + /** + * @brief API to be monitored + * + * @type sai_common_api_t + * @flags CREATE_AND_SET + * @default SAI_COMMON_API_CREATE + */ + SAI_PERFMON_ATTR_COMMON_API, + + /** + * @brief Performance metrics to be collected + * + * @type sai_perfmon_metrics_t + * @flags CREATE_AND_SET + * @default SAI_PERFMON_METRICS_NONE + */ + SAI_PERFMON_ATTR_PERFMON_METRICS, + + /** + * @brief Performance data as collected. This is clear on read. + * Performance data is computed once enabled and is cleared once read. + * + * @type sai_uint64_t + * @flags READ_ONLY + */ + SAI_PERFMON_ATTR_PERFDATA, + + /** + * @brief End of Performance Monitoring attributes + */ + SAI_PERFMON_ATTR_END, + + /** Custom range base value */ + SAI_PERFMON_ATTR_CUSTOM_RANGE_START = 0x10000000, + + /** End of custom range base */ + SAI_PERFMON_ATTR_CUSTOM_RANGE_END + +} sai_perfmon_attr_t; + +/** + * @brief Create performance monitoring object + * + * @param[out] perfmon_id Performance Monitoring 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_perfmon_fn)( + _Out_ sai_object_id_t *perfmon_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + +/** + * @brief Remove performance monitoring object + * + * @param[in] perfmon_id Performance monitoring id + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_remove_perfmon_fn)( + _In_ sai_object_id_t perfmon_id); + +/** + * @brief Set performance monitoring attribute + * + * @param[in] perfmon_id Performance monitoring id + * @param[in] attr Attribute + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_set_perfmon_attribute_fn)( + _In_ sai_object_id_t perfmon_id, + _In_ const sai_attribute_t *attr); + +/** + * @brief Get Performance Monitoring attribute + * + * @param[in] perfmon_id Performance monitoring 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_perfmon_attribute_fn)( + _In_ sai_object_id_t perfmon_id, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list); + +/** + * @brief Performance Monitoring API methods table retrieved with sai_api_query() + */ +typedef struct _sai_perfmon_api_t +{ + /** + * @brief SAI Performance Monitoring API set + */ + sai_create_perfmon_fn create_perfmon; + sai_remove_perfmon_fn remove_perfmon; + sai_set_perfmon_attribute_fn set_perfmon_attribute; + sai_get_perfmon_attribute_fn get_perfmon_attribute; +} sai_perfmon_api_t; + +/** + * @} + */ +#endif /** __SAIPERFMON_H_ */ diff --git a/inc/saiswitch.h b/inc/saiswitch.h index 64072c22c..d92b1c2d8 100644 --- a/inc/saiswitch.h +++ b/inc/saiswitch.h @@ -3598,6 +3598,16 @@ typedef enum _sai_switch_attr_t */ SAI_SWITCH_ATTR_PTP_SYNTONIZE_ADJUST, + /** + * @brief Performance Monitoring enabled on the switch + * + * @type sai_object_list_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_PERFMON + * @default empty + */ + SAI_SWITCH_ATTR_PERFMON_LIST, + /** * @brief End of attributes */ diff --git a/inc/saitypes.h b/inc/saitypes.h index 287a9e0da..3d3c6a7a5 100644 --- a/inc/saitypes.h +++ b/inc/saitypes.h @@ -306,6 +306,7 @@ typedef enum _sai_object_type_t SAI_OBJECT_TYPE_VIRTUAL_CHANNEL = 116, SAI_OBJECT_TYPE_CBFC_CREDIT_POOL = 117, SAI_OBJECT_TYPE_CBFC_CREDIT_PROFILE = 118, + SAI_OBJECT_TYPE_PERFMON = 119, /** Must remain in last position */ SAI_OBJECT_TYPE_MAX, From dcf7229d7e75cd07cf348b3d4f27e0f0d397988d Mon Sep 17 00:00:00 2001 From: JaiOCP Date: Thu, 7 May 2026 17:32:11 -0700 Subject: [PATCH 2/4] SAI API Performance Monitoring Signed-off-by: JaiOCP --- doc/perfmon/SAI-perfmon-Spec.md | 9 ++------- inc/saiperfmon.h | 2 +- inc/saiswitch.h | 3 +-- meta/saisanitycheck.c | 12 ++++++++++++ 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/doc/perfmon/SAI-perfmon-Spec.md b/doc/perfmon/SAI-perfmon-Spec.md index 22ad40a0b..422fc6576 100755 --- a/doc/perfmon/SAI-perfmon-Spec.md +++ b/doc/perfmon/SAI-perfmon-Spec.md @@ -60,7 +60,7 @@ API completion time consists of the time spent in the SAI adapter and the SDK, i These metrics can be used to: - Improve SAI adapter and SDK implementations - Provide a baseline for comparing different hardware -- Instantaneous value: Provides [time, n], where n > 1 represents the number of objects in a bulk API, or n = 1 represents the last observed latency for a single object +- Instantaneous value: last observed latency for the API call - Maximum: The highest value observed across the last n invocations - Average: The average value over the last n invocations. @@ -210,14 +210,9 @@ sai_attr_list[1].value.s32 = SAI_COMMON_API_BULK_SET; // Configure metrics to be measured sai_attr_list[2].id = SAI_PERFMON_ATTR_PERFMON_METRICS; sai_attr_list[2].value.s32 = SAI_PERFMON_METRICS_AVERAGE_LATENCY; - -// Configure Time Interval in msec -sai_attr_list[3].id = SAI_PERFMON_ATTR_METRICS_TIME_INTERVAL; -sai_attr_list[3].value.u32 = 2048; - // Create perfmon object -attr_count = 4; +attr_count = 3; create_perfmon( &sai_perfmon_object, switch_id, diff --git a/inc/saiperfmon.h b/inc/saiperfmon.h index a9c846bc5..65572c7d1 100644 --- a/inc/saiperfmon.h +++ b/inc/saiperfmon.h @@ -97,7 +97,7 @@ typedef enum _sai_perfmon_attr_t SAI_PERFMON_ATTR_PERFMON_METRICS, /** - * @brief Performance data as collected. This is clear on read. + * @brief Latency measure in microseconds. This is clear on read. * Performance data is computed once enabled and is cleared once read. * * @type sai_uint64_t diff --git a/inc/saiswitch.h b/inc/saiswitch.h index d92b1c2d8..2cd4b69aa 100644 --- a/inc/saiswitch.h +++ b/inc/saiswitch.h @@ -3602,9 +3602,8 @@ typedef enum _sai_switch_attr_t * @brief Performance Monitoring enabled on the switch * * @type sai_object_list_t - * @flags CREATE_AND_SET + * @flags READ_ONLY * @objects SAI_OBJECT_TYPE_PERFMON - * @default empty */ SAI_SWITCH_ATTR_PERFMON_LIST, diff --git a/meta/saisanitycheck.c b/meta/saisanitycheck.c index ae1d23b22..d77218bb8 100644 --- a/meta/saisanitycheck.c +++ b/meta/saisanitycheck.c @@ -5554,6 +5554,18 @@ void check_graph_connected() continue; } + if (SAI_OBJECT_TYPE_PERFMON == idx2ot(i)) + { + /* + * Allow performance monitor object to be disconnected from main graph + * as use case is by querying base object stats and not by direct reference + */ + + META_LOG_WARN("perfmon object %s is disconnected from graph", + sai_metadata_all_object_type_infos[i]->objecttypename); + + continue; + } META_ASSERT_FAIL("object %s is disconnected from graph", sai_metadata_all_object_type_infos[i]->objecttypename); } From 96653ea177e11b717c22f9bf35c3ef75b793917b Mon Sep 17 00:00:00 2001 From: JaiOCP Date: Thu, 21 May 2026 15:21:06 -0700 Subject: [PATCH 3/4] SAI API performance monitoring Signed-off-by: JaiOCP --- doc/perfmon/SAI-perfmon-Spec.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/perfmon/SAI-perfmon-Spec.md b/doc/perfmon/SAI-perfmon-Spec.md index 422fc6576..6109ed8cc 100755 --- a/doc/perfmon/SAI-perfmon-Spec.md +++ b/doc/perfmon/SAI-perfmon-Spec.md @@ -171,16 +171,15 @@ typedef enum _sai_perfmon_attr_t ``` #### 4.3.3 Perfmon Object Switch Binding -List of perfmon objects can be bound to the switch object. This binding can be done as a SET operation when perfmon object is created. +List of perfmon objects configured can be read using the following switch atribute. ``` /** * @brief Performance Monitoring enabled on the switch * * @type sai_object_list_t - * @flags CREATE_AND_SET - * @objects SAI_OBJECT_TYPE_PERFMO$ - * @default empty + * @flags READ_ONLY + * @objects SAI_OBJECT_TYPE_PERFMON */ SAI_SWITCH_ATTR_PERFMON_LIST, ``` @@ -196,7 +195,7 @@ This section talks about enabling performance monitoring for a given API and a m ``` /* - * Configure CSIG Compact Tag for ABW signal processing and time interval of 256 micro seconds + * Configure Perfmon object for Bulk Set API performance monitoring */ // Specify the Object of intererst From 156be3d23022c1cb95a46240de48242da13a56be Mon Sep 17 00:00:00 2001 From: JaiOCP Date: Thu, 7 May 2026 17:32:11 -0700 Subject: [PATCH 4/4] SAI API Performance Monitoring Signed-off-by: JaiOCP SAI API performance monitoring Signed-off-by: JaiOCP Fix gensairpc.pl crash on Doxygen 1.9.8+ by reusing NeedsTwoPassProcessing (#2282) Why: To fix below build error Uncaught exception from user code: at gensairpc.pl line 480. main::assign_attr_types(HASH(0x55e190dc20c8), ARRAY(0x55e190d2d080)) called at gensairpc.pl line 434 main::get_definitions() called at gensairpc.pl line 156 main::assign_attr_types(HASH(0x55e190dc20c8), ARRAY(0x55e190d2d080)) called at gensairpc.pl line 434 main::get_definitions() called at gensairpc.pl line 156 How: gensairpc.pl crashed during SAI thrift build with an uncaught exception at line 480 (assign_attr_types) because its inline Doxygen layout detection was too weak - it only checked sai_8h.xml for any enumvalue presence, missing cases where the new Doxygen 1.9.8+ XML structure requires group__*.xml files to be processed for complete definitions. This caused incomplete parsing, leading to missing types and a croak in assign_attr_types when sai_attribute_value_t could not be found. Changes: - xmlutils.pm: Add NeedsTwoPassProcessing and export it. - parse.pl: Remove local NeedsTwoPassProcessing; use imported version. - gensairpc.pl: Replace inline detection with NeedsTwoPassProcessing() call, fixing the build failure and eliminating code duplication. Signed-off-by: Pavan Naregundi Count BFD session state changes from UP to DOWN (#2268) Signed-off-by: Chikkegowda Chikkaiah HW FRR switchover notification support for protection groups (#2269) Signed-off-by: Chikkegowda Chikkaiah Port storm control enhancemnets (#2258) (#2258) Signed-off-by: rpmarvell --- ...Proposal-Port-Storm-Control-Enhancement.md | 134 +++++++ doc/SAI-Proposal-HW-FRR.md | 341 ++++++++++-------- doc/perfmon/SAI-perfmon-Spec.md | 18 +- inc/saibfd.h | 5 +- inc/sainexthopgroup.h | 44 +++ inc/saiperfmon.h | 2 +- inc/saiport.h | 60 +++ inc/saiswitch.h | 12 +- meta/gensairpc.pl | 12 +- meta/parse.pl | 112 +----- meta/saisanitycheck.c | 12 + .../sai_rpc_server_helper_functions.tt | 2 +- meta/xmlutils.pm | 104 ++++++ 13 files changed, 586 insertions(+), 272 deletions(-) create mode 100644 doc/QOS/SAI-Proposal-Port-Storm-Control-Enhancement.md diff --git a/doc/QOS/SAI-Proposal-Port-Storm-Control-Enhancement.md b/doc/QOS/SAI-Proposal-Port-Storm-Control-Enhancement.md new file mode 100644 index 000000000..6d04be2a2 --- /dev/null +++ b/doc/QOS/SAI-Proposal-Port-Storm-Control-Enhancement.md @@ -0,0 +1,134 @@ +# [SAI] Port Storm Control Enhancement +------------------------------------------------------------------------------- + Title | Port storm control enhancement +-------------|----------------------------------------------------------------- + Authors | Rajesh Perumal (Marvell) + Status | Reviewed on 26-Feb-2026 + Type | Standards track + Created | 24-Feb-2026 + SAI-Version | 1.18 +------------------------------------------------------------------------------- + +## Introduction + + +There is a long‑standing mismatch between the SAI storm‑control attribute definitions and how these attributes are interpreted within SONiC. The existing SAI attributes for flood and multicast storm control do not clearly differentiate among unknown unicast, unknown multicast, and known multicast traffic. As a result, vendors and SONiC implementations interpret these attributes differently, leading to inconsistent behavior across platforms. +To improve clarity and alignment, two approaches can be followed: + +To improve clarity and alignment, new, explicit attributes are introduced for each traffic class and the ambiguous ones are deprecated. This resolves the ambiguity present in the current specification and enables consistent implementation across vendors and multiple NOS. +The following section details the selected enhancement approach and the required SAI header updates. + +## Motivation + +The table below highlights the gap between the current SAI attribute descriptions and how these attributes are interpreted within SONiC: + +| Existing SAI Attribute | SAI Description | SONiC usage | +|-------------------------------------------------|------------------------------------------------|------------------------------| +| SAI_PORT_ATTR_FLOOD_STORM_CONTROL_POLICER_ID | Unknown unicast/unknown multicast flood control| Unknown unicast flood control| +| SAI_PORT_ATTR_MULTICAST_STORM_CONTROL_POLICER_ID| Multicast storm control policer on port | Unknown multicast traffic | + +This mismatch creates ambiguity, leads to inconsistent vendor implementations, and results in unclear handling of traffic classes (unknown unicast, unknown multicast, and known multicast). To address this, either the existing SAI attribute definitions must be refined, or new traffic‑specific attributes should be introduced. However, redefining current SAI attributes may introduce backward‑compatibility issues. + +By defining explicit attributes, SAI can present storm‑control behavior more clearly across all network operating systems (including SONiC), reduce implementation confusion, and support future enhancements in storm‑control processing. + + +## SAI Enhancement + +This enhancement proposes to deprecate the existing storm control attributes SAI_PORT_ATTR_FLOOD_STORM_CONTROL_POLICER_ID and SAI_PORT_ATTR_MULTICAST_STORM_CONTROL_POLICER_ID, and replace it with dedicated, traffic‑specific attributes for unknown unicast, known unicast, unknown multicast and known multicast storm control. This provides clear separation of behavior and removes the ambiguity present in the current definition. + +### Deprecated Attributes +``` +--- Old Code ++++ New Code + /** + * @brief Enable flood (unknown unicast or unknown multicast) + * storm control policer on port. ++ * Deprecated. Use SAI_PORT_ATTR_UNKNOWN_UNICAST_STORM_CONTROL_POLICER_ID and SAI_PORT_ATTR_UNKNOWN_MULTICAST_STORM_CONTROL_POLICER_ID + * + * Set policer id = #SAI_NULL_OBJECT_ID to disable policer on port. + * + * @type sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_POLICER + * @allownull true + * @default SAI_NULL_OBJECT_ID ++ * @deprecated true + */ + SAI_PORT_ATTR_FLOOD_STORM_CONTROL_POLICER_ID, +``` +``` +--- Old Code ++++ New Code + /** + * @brief Enable multicast storm control policer on port. ++ * Deprecated. Use SAI_PORT_ATTR_KNOWN_MULTICAST_STORM_CONTROL_POLICER_ID and SAI_PORT_ATTR_UNKNOWN_MULTICAST_STORM_CONTROL_POLICER_ID + * + * Set policer id = #SAI_NULL_OBJECT_ID to disable policer on port. + * + * @type sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_POLICER + * @allownull true + * @default SAI_NULL_OBJECT_ID ++ * @deprecated true + */ + SAI_PORT_ATTR_MULTICAST_STORM_CONTROL_POLICER_ID, +``` + +### New port storm control attributes +``` + /** + * @brief Enable unknown unicast storm control policer on port. + * + * Set policer id = #SAI_NULL_OBJECT_ID to disable policer on port. + * + * @type sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_POLICER + * @allownull true + * @default SAI_NULL_OBJECT_ID + */ + SAI_PORT_ATTR_UNKNOWN_UNICAST_STORM_CONTROL_POLICER_ID, + + /** + * @brief Enable known unicast storm control policer on port. + * + * Set policer id = #SAI_NULL_OBJECT_ID to disable policer on port. + * + * @type sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_POLICER + * @allownull true + * @default SAI_NULL_OBJECT_ID + */ + SAI_PORT_ATTR_KNOWN_UNICAST_STORM_CONTROL_POLICER_ID, + + /** + * @brief Enable unknown multicast storm control policer on port. + * + * Set Policer id = #SAI_NULL_OBJECT_ID to disable policer on port. + * + * @type sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_POLICER + * @allownull true + * @default SAI_NULL_OBJECT_ID + */ + SAI_PORT_ATTR_UNKNOWN_MULTICAST_STORM_CONTROL_POLICER_ID, + + /** + * @brief Enable known multicast storm control policer on port. + * + * Set Policer id = #SAI_NULL_OBJECT_ID to disable policer on port. + * + * @type sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_POLICER + * @allownull true + * @default SAI_NULL_OBJECT_ID + */ + SAI_PORT_ATTR_KNOWN_MULTICAST_STORM_CONTROL_POLICER_ID, + +``` + +This approach provides more granular storm‑control handling per traffic type. diff --git a/doc/SAI-Proposal-HW-FRR.md b/doc/SAI-Proposal-HW-FRR.md index c0cf837a8..88943c1f9 100755 --- a/doc/SAI-Proposal-HW-FRR.md +++ b/doc/SAI-Proposal-HW-FRR.md @@ -11,150 +11,197 @@ ## 1.0 Introduction -SAI supports SW based FRR where the decision to switch over to the secondary path is triggered by the SW. - -Following is the current SAI workflow for SW based FRR. -- Create a protection NH -nhg_entry_attrs[0].id = SAI_NEXT_HOP_GROUP_ATTR_TYPE; -nhg_entry_attrs[0].value.u32 = SAI_NEXT_HOP_GROUP_TYPE_PROTECTION; - -- Create primary and secondary members (Note members can be NHG as well) -nhgm_entry_attrs[2].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_CONFIGURED_ROLE; -nhgm_entry_attrs[2].value.u32 = SAI_NEXT_HOP_GROUP_MEMBER_CONFIGURED_ROLE_PRIMARY; -nhgm_entry_attrs[2].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_CONFIGURED_ROLE; -nhgm_entry_attrs[2].value.u32 = SAI_NEXT_HOP_GROUP_MEMBER_CONFIGURED_ROLE_PRIMARY; - -- Based on the monitoring object, SW sets the following boolean to trigger switchover -nhg_entry_attrs[1].id = SAI_NEXT_HOP_GROUP_ATTR_SET_SWITCHOVER; -nhg_entry_attrs[1].value.u32 = true; -saistatus = sai_set_next_hop_group_attribute_fn(nhg_id, nhg_entry_attrs); - -## 2.0 HW Based Trigger -Main change in this proposal is the trigger. Now SW is not responsible for monitoring an object and triggering the switchover. -HW monitors the configured object and triggers the switch to secondary path based on the state of the monitored object. -For example if a port is being monitored and port goes down then all the NH resolving via this port will be switched over to the secondary path. - -## 3.0 SAI Enhancements -Hardware needs to know ahead of time which NHG/NH are part of the secondary group so as to mark them as backup from the configured primary group. For this reason a hint is needed to identify such NHG/NH. - -This hint is provided using a new NHG type -```c - /** Next hop hardware protection group. This is the group backing up the primary in the protection group type and is managed by hardware */ - SAI_NEXT_HOP_GROUP_TYPE_HW_PROTECTION, -``` - -Additionally port counters are introduced to capture -- How many times port has participated in the failover -- Drops observed during failover - -```c - /** SAI port stat if HW protection switchover events */ - SAI_PORT_STAT_IF_IN_HW_PROTECTION_SWITCHOVER_EVENTS, - - /** SAI port stat if HW protection switchover related packet drops */ - SAI_PORT_STAT_IF_IN_HW_PROTECTION_SWITCHOVER_DROP_PKTS, -``` - -## 4.0 Example Workflow - - -### Topology Example -There are two uplinks from a switch and both are part of the primary and secondary group. -For such case we will -- Create a NHG nhg1 of type PROTECTION and configure NH1/port1 and NH2/port2 NH as primary members -- Create a NHG nhg2 of type HW_PROTECTION with members as NH1/port1 and NH2/port2 -- Set NHG nhg2 as a secondary member of NHG nhg1 - -PROTECTION[nhg1] --> PRIMARY[NH1, NH2], SECONDARY[nhg2] -HW_PROTECTION[nhg2] --> [NH1, NH2] - - - - -```c -nh_1_interface_id = 1 -nh_2_interface_id = 2 -switch_id = 0; - -nhg_entry_attrs[0].id = SAI_NEXT_HOP_GROUP_ATTR_TYPE; -nhg_entry_attrs[0].value.u32 = SAI_NEXT_HOP_GROUP_TYPE_PROTECTION; -saistatus = sai_frr_api->create_next_hop_group(&nhg1, switch_id, 1, nhg_entry_attrs); -if (saistatus != SAI_STATUS_SUCCESS) { - return saistatus; -} - -nhg_entry_attrs[0].id = SAI_NEXT_HOP_GROUP_ATTR_TYPE; -nhg_entry_attrs[0].value.u32 = SAI_NEXT_HOP_GROUP_TYPE_HW_PROTECTION; -saistatus = sai_frr_api->create_next_hop_group(&nhg2, switch_id, 1, nhg_entry_attrs); -if (saistatus != SAI_STATUS_SUCCESS) { - return saistatus; -} - -nh_entry_attrs[0].id = SAI_NEXT_HOP_ATTR_TYPE; -nh_entry_attrs[0].value.u32 = SAI_NEXT_HOP_TYPE_IP; -nh_entry_attrs[1].id = SAI_NEXT_HOP_ATTR_IP; -CONVERT_STRING_TO_SAI_IPV4(nh_entry_attrs[1].value, "10.1.1.1"); -nh_entry_attrs[2].id = SAI_NEXT_HOP_ATTR_ROUTER_INTERFACE_ID; -nh_entry_attrs[2].value.u64 = nh_1_interface_id; -saistatus = sai_frr_api->create_next_hop(&nh_1_id, switch_id, 2, nh_entry_attrs); -if (saistatus != SAI_STATUS_SUCCESS) { - return saistatus; -} - -nh_entry_attrs[0].id = SAI_NEXT_HOP_ATTR_TYPE; -nh_entry_attrs[0].value.u32 = SAI_NEXT_HOP_TYPE_IP; -nh_entry_attrs[1].id = SAI_NEXT_HOP_ATTR_IP; -CONVERT_STRING_TO_SAI_IPV4(nh_entry_attrs[1].value, "10.1.2.1"); -nh_entry_attrs[2].id = SAI_NEXT_HOP_ATTR_ROUTER_INTERFACE_ID; -nh_entry_attrs[2].value.u64 = nh_2_interface_id; -saistatus = sai_frr_api->create_next_hop(&nh_2_id, switch_id, 2, nh_entry_attrs); -if (saistatus != SAI_STATUS_SUCCESS) { - return saistatus; -} - -// Program the primary NH Group member. -nhgm_entry_attrs[0].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID; -nhgm_entry_attrs[0].value.oid = nhg1; -nhgm_entry_attrs[1].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID; -nhgm_entry_attrs[1].value.oid = nh_1_id; -nhgm_entry_attrs[2].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_CONFIGURED_ROLE; -nhgm_entry_attrs[2].value.u32 = SAI_NEXT_HOP_GROUP_MEMBER_CONFIGURED_ROLE_PRIMARY; -saistatus = sai_frr_api->create_next_hop_group_member(&nhgm_1_id, switch_id, 2, nhgm_entry_attrs); -if (saistatus != SAI_STATUS_SUCCESS) { - return saistatus; -} - -nhgm_entry_attrs[0].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID; -nhgm_entry_attrs[0].value.oid = nhg1; -nhgm_entry_attrs[1].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID; -nhgm_entry_attrs[1].value.oid = nh_2_id; -nhgm_entry_attrs[2].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_CONFIGURED_ROLE; -nhgm_entry_attrs[2].value.u32 = SAI_NEXT_HOP_GROUP_MEMBER_CONFIGURED_ROLE_PRIMARY; -saistatus = sai_frr_api->create_next_hop_group_member(&nhgm_1_id, switch_id, 2, nhgm_entry_attrs); -if (saistatus != SAI_STATUS_SUCCESS) { - return saistatus; -} - -// Program the secondary NH Group member. -nhgm_entry_attrs[0].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID; -nhgm_entry_attrs[0].value.oid = nhg1; -nhgm_entry_attrs[1].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID; -nhgm_entry_attrs[1].value.oid = nhg2; -nhgm_entry_attrs[2].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_CONFIGURED_ROLE; -nhgm_entry_attrs[2].value.u32 = SAI_NEXT_HOP_GROUP_MEMBER_CONFIGURED_ROLE_PRIMARY; -saistatus = sai_frr_api->create_next_hop_group_member(&nhgm_2_id, switch_id, 2, nhgm_entry_attrs); -if (saistatus != SAI_STATUS_SUCCESS) { - return saistatus; -} - -nhgm_entry_attrs[0].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID; -nhgm_entry_attrs[0].value.oid = nhg_id; -nhgm_entry_attrs[1].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID; -nhgm_entry_attrs[1].value.oid = nh_2_id; -nhgm_entry_attrs[2].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_CONFIGURED_ROLE; -nhgm_entry_attrs[2].value.u32 = SAI_NEXT_HOP_GROUP_MEMBER_CONFIGURED_ROLE_PRIMARY; -saistatus = sai_frr_api->create_next_hop_group_member(&nhgm_3_id, switch_id, 2, nhgm_entry_attrs); -if (saistatus != SAI_STATUS_SUCCESS) { - return saistatus; -} -``` +SAI supports SW based FRR where the decision to switch over to the secondary path is triggered by the SW. + +Following is the current SAI workflow for SW based FRR. +- Create a protection NH +nhg_entry_attrs[0].id = SAI_NEXT_HOP_GROUP_ATTR_TYPE; +nhg_entry_attrs[0].value.u32 = SAI_NEXT_HOP_GROUP_TYPE_PROTECTION; + +- Create primary and secondary members (Note members can be NHG as well) +nhgm_entry_attrs[2].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_CONFIGURED_ROLE; +nhgm_entry_attrs[2].value.u32 = SAI_NEXT_HOP_GROUP_MEMBER_CONFIGURED_ROLE_PRIMARY; +nhgm_entry_attrs[2].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_CONFIGURED_ROLE; +nhgm_entry_attrs[2].value.u32 = SAI_NEXT_HOP_GROUP_MEMBER_CONFIGURED_ROLE_PRIMARY; + +- Based on the monitoring object, SW sets the following boolean to trigger switchover +nhg_entry_attrs[1].id = SAI_NEXT_HOP_GROUP_ATTR_SET_SWITCHOVER; +nhg_entry_attrs[1].value.u32 = true; +saistatus = sai_set_next_hop_group_attribute_fn(nhg_id, nhg_entry_attrs); + +## 2.0 HW Based Trigger +Main change in this proposal is the trigger. Now SW is not responsible for monitoring an object and triggering the switchover. +HW monitors the configured object and triggers the switch to secondary path based on the state of the monitored object. +For example if a port is being monitored and port goes down then all the NH resolving via this port will be switched over to the secondary path. + +## 3.0 SAI Enhancements +Hardware needs to know ahead of time which NHG/NH are part of the secondary group so as to mark them as backup from the configured primary group. For this reason a hint is needed to identify such NHG/NH. + +This hint is provided using a new NHG type +```c + /** Next hop hardware protection group. This is the group backing up the primary in the protection group type and is managed by hardware */ + SAI_NEXT_HOP_GROUP_TYPE_HW_PROTECTION, +``` + +Additionally port counters are introduced to capture +- How many times port has participated in the failover +- Drops observed during failover + +```c + /** SAI port stat if HW protection switchover events */ + SAI_PORT_STAT_IF_IN_HW_PROTECTION_SWITCHOVER_EVENTS, + + /** SAI port stat if HW protection switchover related packet drops */ + SAI_PORT_STAT_IF_IN_HW_PROTECTION_SWITCHOVER_DROP_PKTS, +``` + +## 4.0 Example Workflow + + +### Topology Example +There are two uplinks from a switch and both are part of the primary and secondary group. +For such case we will +- Create a NHG nhg1 of type PROTECTION and configure NH1/port1 and NH2/port2 NH as primary members +- Create a NHG nhg2 of type HW_PROTECTION with members as NH1/port1 and NH2/port2 +- Set NHG nhg2 as a secondary member of NHG nhg1 + +PROTECTION[nhg1] --> PRIMARY[NH1, NH2], SECONDARY[nhg2] +HW_PROTECTION[nhg2] --> [NH1, NH2] + + + + +```c +nh_1_interface_id = 1 +nh_2_interface_id = 2 +switch_id = 0; + +nhg_entry_attrs[0].id = SAI_NEXT_HOP_GROUP_ATTR_TYPE; +nhg_entry_attrs[0].value.u32 = SAI_NEXT_HOP_GROUP_TYPE_PROTECTION; +saistatus = sai_frr_api->create_next_hop_group(&nhg1, switch_id, 1, nhg_entry_attrs); +if (saistatus != SAI_STATUS_SUCCESS) { + return saistatus; +} + +nhg_entry_attrs[0].id = SAI_NEXT_HOP_GROUP_ATTR_TYPE; +nhg_entry_attrs[0].value.u32 = SAI_NEXT_HOP_GROUP_TYPE_HW_PROTECTION; +saistatus = sai_frr_api->create_next_hop_group(&nhg2, switch_id, 1, nhg_entry_attrs); +if (saistatus != SAI_STATUS_SUCCESS) { + return saistatus; +} + +nh_entry_attrs[0].id = SAI_NEXT_HOP_ATTR_TYPE; +nh_entry_attrs[0].value.u32 = SAI_NEXT_HOP_TYPE_IP; +nh_entry_attrs[1].id = SAI_NEXT_HOP_ATTR_IP; +CONVERT_STRING_TO_SAI_IPV4(nh_entry_attrs[1].value, "10.1.1.1"); +nh_entry_attrs[2].id = SAI_NEXT_HOP_ATTR_ROUTER_INTERFACE_ID; +nh_entry_attrs[2].value.u64 = nh_1_interface_id; +saistatus = sai_frr_api->create_next_hop(&nh_1_id, switch_id, 2, nh_entry_attrs); +if (saistatus != SAI_STATUS_SUCCESS) { + return saistatus; +} + +nh_entry_attrs[0].id = SAI_NEXT_HOP_ATTR_TYPE; +nh_entry_attrs[0].value.u32 = SAI_NEXT_HOP_TYPE_IP; +nh_entry_attrs[1].id = SAI_NEXT_HOP_ATTR_IP; +CONVERT_STRING_TO_SAI_IPV4(nh_entry_attrs[1].value, "10.1.2.1"); +nh_entry_attrs[2].id = SAI_NEXT_HOP_ATTR_ROUTER_INTERFACE_ID; +nh_entry_attrs[2].value.u64 = nh_2_interface_id; +saistatus = sai_frr_api->create_next_hop(&nh_2_id, switch_id, 2, nh_entry_attrs); +if (saistatus != SAI_STATUS_SUCCESS) { + return saistatus; +} + +// Program the primary NH Group member. +nhgm_entry_attrs[0].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID; +nhgm_entry_attrs[0].value.oid = nhg1; +nhgm_entry_attrs[1].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID; +nhgm_entry_attrs[1].value.oid = nh_1_id; +nhgm_entry_attrs[2].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_CONFIGURED_ROLE; +nhgm_entry_attrs[2].value.u32 = SAI_NEXT_HOP_GROUP_MEMBER_CONFIGURED_ROLE_PRIMARY; +saistatus = sai_frr_api->create_next_hop_group_member(&nhgm_1_id, switch_id, 2, nhgm_entry_attrs); +if (saistatus != SAI_STATUS_SUCCESS) { + return saistatus; +} + +nhgm_entry_attrs[0].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID; +nhgm_entry_attrs[0].value.oid = nhg1; +nhgm_entry_attrs[1].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID; +nhgm_entry_attrs[1].value.oid = nh_2_id; +nhgm_entry_attrs[2].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_CONFIGURED_ROLE; +nhgm_entry_attrs[2].value.u32 = SAI_NEXT_HOP_GROUP_MEMBER_CONFIGURED_ROLE_PRIMARY; +saistatus = sai_frr_api->create_next_hop_group_member(&nhgm_1_id, switch_id, 2, nhgm_entry_attrs); +if (saistatus != SAI_STATUS_SUCCESS) { + return saistatus; +} + +// Program the secondary NH Group member. +nhgm_entry_attrs[0].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID; +nhgm_entry_attrs[0].value.oid = nhg1; +nhgm_entry_attrs[1].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID; +nhgm_entry_attrs[1].value.oid = nhg2; +nhgm_entry_attrs[2].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_CONFIGURED_ROLE; +nhgm_entry_attrs[2].value.u32 = SAI_NEXT_HOP_GROUP_MEMBER_CONFIGURED_ROLE_PRIMARY; +saistatus = sai_frr_api->create_next_hop_group_member(&nhgm_2_id, switch_id, 2, nhgm_entry_attrs); +if (saistatus != SAI_STATUS_SUCCESS) { + return saistatus; +} + +nhgm_entry_attrs[0].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID; +nhgm_entry_attrs[0].value.oid = nhg_id; +nhgm_entry_attrs[1].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID; +nhgm_entry_attrs[1].value.oid = nh_2_id; +nhgm_entry_attrs[2].id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_CONFIGURED_ROLE; +nhgm_entry_attrs[2].value.u32 = SAI_NEXT_HOP_GROUP_MEMBER_CONFIGURED_ROLE_PRIMARY; +saistatus = sai_frr_api->create_next_hop_group_member(&nhgm_3_id, switch_id, 2, nhgm_entry_attrs); +if (saistatus != SAI_STATUS_SUCCESS) { + return saistatus; +} +``` + +## 5.0 Per-Monitored-Object Switchover Notification + +To provide NOS visibility for HW-triggered FRR, add a switch-level notification callback. +Notification is generated per monitored object, and one monitored object may point to multiple protection groups. + +### Notification + +```c +typedef struct _sai_next_hop_group_hw_protection_switchover_notification_data_t +{ + sai_object_id_t monitored_oid; // monitored object id + sai_next_hop_group_member_observed_role_t new_role // Current role after the switchover + uint32_t switchover_success_count; // number of protection groups switched successfully + sai_object_list_t failed_next_hop_groups; // failed protection-group object ids +} sai_next_hop_group_hw_protection_switchover_notification_data_t; + +typedef void (*sai_next_hop_group_hw_protection_switchover_notification_fn)( + _In_ uint32_t count, + _In_ const sai_next_hop_group_hw_protection_switchover_notification_data_t *data); +``` + +### Switch attribute for callback registration + +```c +SAI_SWITCH_ATTR_NEXT_HOP_GROUP_HW_PROTECTION_SWITCHOVER_NOTIFY +``` + +### Usage example + +```c +switch_attr.id = SAI_SWITCH_ATTR_NEXT_HOP_GROUP_HW_PROTECTION_SWITCHOVER_NOTIFY; +switch_attr.value.ptr = (void*)nhg_hw_protection_switchover_cb; +sai_switch_api->set_switch_attribute(switch_id, &switch_attr); +``` + +Example callback report: +```c + monitored_oid = + new_role = SAI_NEXT_HOP_GROUP_MEMBER_CONFIGURED_ROLE_STANDBY + switchover_success_count = 5 + failed_next_hop_groups.count = 2 + failed_next_hop_groups.list[0] = failed_nhg_oid1 + failed_next_hop_groups.list[1] = failed_nhg_oid2 +``` +In this example, `switchover_success_count = 5` means five protection groups switched over successfully +for the monitored object, while `failed_next_hop_groups.count = 2` means two protection groups failed. diff --git a/doc/perfmon/SAI-perfmon-Spec.md b/doc/perfmon/SAI-perfmon-Spec.md index 22ad40a0b..6109ed8cc 100755 --- a/doc/perfmon/SAI-perfmon-Spec.md +++ b/doc/perfmon/SAI-perfmon-Spec.md @@ -60,7 +60,7 @@ API completion time consists of the time spent in the SAI adapter and the SDK, i These metrics can be used to: - Improve SAI adapter and SDK implementations - Provide a baseline for comparing different hardware -- Instantaneous value: Provides [time, n], where n > 1 represents the number of objects in a bulk API, or n = 1 represents the last observed latency for a single object +- Instantaneous value: last observed latency for the API call - Maximum: The highest value observed across the last n invocations - Average: The average value over the last n invocations. @@ -171,16 +171,15 @@ typedef enum _sai_perfmon_attr_t ``` #### 4.3.3 Perfmon Object Switch Binding -List of perfmon objects can be bound to the switch object. This binding can be done as a SET operation when perfmon object is created. +List of perfmon objects configured can be read using the following switch atribute. ``` /** * @brief Performance Monitoring enabled on the switch * * @type sai_object_list_t - * @flags CREATE_AND_SET - * @objects SAI_OBJECT_TYPE_PERFMO$ - * @default empty + * @flags READ_ONLY + * @objects SAI_OBJECT_TYPE_PERFMON */ SAI_SWITCH_ATTR_PERFMON_LIST, ``` @@ -196,7 +195,7 @@ This section talks about enabling performance monitoring for a given API and a m ``` /* - * Configure CSIG Compact Tag for ABW signal processing and time interval of 256 micro seconds + * Configure Perfmon object for Bulk Set API performance monitoring */ // Specify the Object of intererst @@ -210,14 +209,9 @@ sai_attr_list[1].value.s32 = SAI_COMMON_API_BULK_SET; // Configure metrics to be measured sai_attr_list[2].id = SAI_PERFMON_ATTR_PERFMON_METRICS; sai_attr_list[2].value.s32 = SAI_PERFMON_METRICS_AVERAGE_LATENCY; - -// Configure Time Interval in msec -sai_attr_list[3].id = SAI_PERFMON_ATTR_METRICS_TIME_INTERVAL; -sai_attr_list[3].value.u32 = 2048; - // Create perfmon object -attr_count = 4; +attr_count = 3; create_perfmon( &sai_perfmon_object, switch_id, diff --git a/inc/saibfd.h b/inc/saibfd.h index 2580afe96..5a9c9d6a7 100644 --- a/inc/saibfd.h +++ b/inc/saibfd.h @@ -589,7 +589,10 @@ typedef enum _sai_bfd_session_stat_t SAI_BFD_SESSION_STAT_OUT_PACKETS, /** Packet Drop stat count */ - SAI_BFD_SESSION_STAT_DROP_PACKETS + SAI_BFD_SESSION_STAT_DROP_PACKETS, + + /** Count transitions where the session state changes from UP to DOWN */ + SAI_BFD_SESSION_STAT_SESSION_UP2DOWN_TRANSITIONS } sai_bfd_session_stat_t; diff --git a/inc/sainexthopgroup.h b/inc/sainexthopgroup.h index 057067869..a34e9fdf6 100644 --- a/inc/sainexthopgroup.h +++ b/inc/sainexthopgroup.h @@ -111,6 +111,36 @@ typedef enum _sai_next_hop_group_admin_role_t } sai_next_hop_group_admin_role_t; +/** + * @brief Defines the HW protection switchover status + */ +typedef struct _sai_next_hop_group_hw_protection_switchover_notification_data_t +{ + /** + * @brief Monitored object id + * + * @objects SAI_OBJECT_TYPE_PORT, SAI_OBJECT_TYPE_LAG, SAI_OBJECT_TYPE_ROUTER_INTERFACE, SAI_OBJECT_TYPE_VLAN_MEMBER, SAI_OBJECT_TYPE_TUNNEL, SAI_OBJECT_TYPE_BRIDGE_PORT, SAI_OBJECT_TYPE_ICMP_ECHO_SESSION, SAI_OBJECT_TYPE_BFD_SESSION + */ + sai_object_id_t monitored_oid; + + /** + * @brief Current role after the switchover + */ + sai_next_hop_group_member_observed_role_t new_role; + + /** + * @brief Number of protection groups that switched over successfully + */ + uint32_t switchover_success_count; + + /** + * @brief List of protection groups that failed switchover. + * + * @objects SAI_OBJECT_TYPE_NEXT_HOP_GROUP + */ + sai_object_list_t failed_next_hop_groups; +} sai_next_hop_group_hw_protection_switchover_notification_data_t; + /** * @brief Attribute id for next hop */ @@ -683,6 +713,20 @@ typedef sai_status_t (*sai_get_next_hop_group_map_attribute_fn)( _In_ uint32_t attr_count, _Inout_ sai_attribute_t *attr_list); +/** + * @brief Next Hop Group HW protection switchover notification callback + * + * Passed as a parameter into sai_initialize_switch(). + * + * @count data[count] + * + * @param[in] count Number of notifications + * @param[in] data Array of notification data + */ +typedef void (*sai_next_hop_group_hw_protection_switchover_notification_fn)( + _In_ uint32_t count, + _In_ const sai_next_hop_group_hw_protection_switchover_notification_data_t *data); + /** * @brief Next Hop methods table retrieved with sai_api_query() */ diff --git a/inc/saiperfmon.h b/inc/saiperfmon.h index a9c846bc5..65572c7d1 100644 --- a/inc/saiperfmon.h +++ b/inc/saiperfmon.h @@ -97,7 +97,7 @@ typedef enum _sai_perfmon_attr_t SAI_PERFMON_ATTR_PERFMON_METRICS, /** - * @brief Performance data as collected. This is clear on read. + * @brief Latency measure in microseconds. This is clear on read. * Performance data is computed once enabled and is cleared once read. * * @type sai_uint64_t diff --git a/inc/saiport.h b/inc/saiport.h index 2d85f3f64..8c14e8ad2 100644 --- a/inc/saiport.h +++ b/inc/saiport.h @@ -1382,6 +1382,9 @@ typedef enum _sai_port_attr_t * @brief Enable flood (unknown unicast or unknown multicast) * storm control policer on port. * + * Deprecated. Use SAI_PORT_ATTR_UNKNOWN_UNICAST_STORM_CONTROL_POLICER_ID + * and SAI_PORT_ATTR_UNKNOWN_MULTICAST_STORM_CONTROL_POLICER_ID. + * * Set policer id = #SAI_NULL_OBJECT_ID to disable policer on port. * * @type sai_object_id_t @@ -1389,6 +1392,7 @@ typedef enum _sai_port_attr_t * @objects SAI_OBJECT_TYPE_POLICER * @allownull true * @default SAI_NULL_OBJECT_ID + * @deprecated true */ SAI_PORT_ATTR_FLOOD_STORM_CONTROL_POLICER_ID, @@ -1408,6 +1412,9 @@ typedef enum _sai_port_attr_t /** * @brief Enable multicast storm control policer on port. * + * Deprecated. Use SAI_PORT_ATTR_KNOWN_MULTICAST_STORM_CONTROL_POLICER_ID + * and SAI_PORT_ATTR_UNKNOWN_MULTICAST_STORM_CONTROL_POLICER_ID. + * * Set policer id = #SAI_NULL_OBJECT_ID to disable policer on port. * * @type sai_object_id_t @@ -1415,6 +1422,7 @@ typedef enum _sai_port_attr_t * @objects SAI_OBJECT_TYPE_POLICER * @allownull true * @default SAI_NULL_OBJECT_ID + * @deprecated true */ SAI_PORT_ATTR_MULTICAST_STORM_CONTROL_POLICER_ID, @@ -3267,6 +3275,58 @@ typedef enum _sai_port_attr_t */ SAI_PORT_ATTR_CBFC_CREDIT_POOL_LIST, + /** + * @brief Enable known unicast storm control policer on port. + * + * Set policer id = #SAI_NULL_OBJECT_ID to disable policer on port. + * + * @type sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_POLICER + * @allownull true + * @default SAI_NULL_OBJECT_ID + */ + SAI_PORT_ATTR_KNOWN_UNICAST_STORM_CONTROL_POLICER_ID, + + /** + * @brief Enable unknown unicast storm control policer on port. + * + * Set policer id = #SAI_NULL_OBJECT_ID to disable policer on port. + * + * @type sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_POLICER + * @allownull true + * @default SAI_NULL_OBJECT_ID + */ + SAI_PORT_ATTR_UNKNOWN_UNICAST_STORM_CONTROL_POLICER_ID, + + /** + * @brief Enable known multicast storm control policer on port. + * + * Set Policer id = #SAI_NULL_OBJECT_ID to disable policer on port. + * + * @type sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_POLICER + * @allownull true + * @default SAI_NULL_OBJECT_ID + */ + SAI_PORT_ATTR_KNOWN_MULTICAST_STORM_CONTROL_POLICER_ID, + + /** + * @brief Enable unknown multicast storm control policer on port. + * + * Set Policer id = #SAI_NULL_OBJECT_ID to disable policer on port. + * + * @type sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_POLICER + * @allownull true + * @default SAI_NULL_OBJECT_ID + */ + SAI_PORT_ATTR_UNKNOWN_MULTICAST_STORM_CONTROL_POLICER_ID, + /** * @brief End of attributes */ diff --git a/inc/saiswitch.h b/inc/saiswitch.h index d92b1c2d8..87ed34028 100644 --- a/inc/saiswitch.h +++ b/inc/saiswitch.h @@ -3598,13 +3598,21 @@ typedef enum _sai_switch_attr_t */ SAI_SWITCH_ATTR_PTP_SYNTONIZE_ADJUST, + /** + * @brief HW protection switchover notification callback function passed to the adapter. + * + * @type sai_pointer_t sai_next_hop_group_hw_protection_switchover_notification_fn + * @flags CREATE_AND_SET + * @default NULL + */ + SAI_SWITCH_ATTR_NEXT_HOP_GROUP_HW_PROTECTION_SWITCHOVER_NOTIFY, + /** * @brief Performance Monitoring enabled on the switch * * @type sai_object_list_t - * @flags CREATE_AND_SET + * @flags READ_ONLY * @objects SAI_OBJECT_TYPE_PERFMON - * @default empty */ SAI_SWITCH_ATTR_PERFMON_LIST, diff --git a/meta/gensairpc.pl b/meta/gensairpc.pl index de9dacd64..305011020 100755 --- a/meta/gensairpc.pl +++ b/meta/gensairpc.pl @@ -377,10 +377,18 @@ sub get_definitions { my %apis; my $i = 0; - # Iterate over files - for ( GetSaiXmlFiles($XMLDIR) ) { + my @xml_files = GetSaiXmlFiles($XMLDIR); + if (NeedsTwoPassProcessing()) + { + unshift @xml_files, GetGroupXmlFiles($XMLDIR); + } + + for ( @xml_files ) + { my $xml = ReadXml($_); + next unless defined $xml->{compounddef}[0]; + # Iterate over definitions for ( @{ $xml->{compounddef}[0]->{sectiondef} } ) { if ( $_->{kind} eq 'typedef' or $_->{kind} eq 'enum' ) { diff --git a/meta/parse.pl b/meta/parse.pl index 86c92a512..3b23e0766 100755 --- a/meta/parse.pl +++ b/meta/parse.pl @@ -2695,6 +2695,7 @@ sub ProcessStructValueType my $type = shift; return "SAI_ATTR_VALUE_TYPE_OBJECT_ID" if $type eq "sai_object_id_t"; + return "SAI_ATTR_VALUE_TYPE_OBJECT_LIST" if $type eq "sai_object_list_t"; return "SAI_ATTR_VALUE_TYPE_MAC" if $type eq "sai_mac_t"; return "SAI_ATTR_VALUE_TYPE_IP_ADDRESS" if $type eq "sai_ip_address_t"; return "SAI_ATTR_VALUE_TYPE_IP_PREFIX" if $type eq "sai_ip_prefix_t"; @@ -2743,7 +2744,7 @@ sub ProcessStructObjects my $type = $struct->{type}; - return "NULL" if not $type eq "sai_object_id_t" and not $type eq "sai_attribute_t*"; + return "NULL" if not $type eq "sai_object_id_t" and not $type eq "sai_object_list_t" and not $type eq "sai_attribute_t*"; WriteSource "const sai_object_type_t sai_metadata_struct_member_sai_${rawname}_t_${key}_allowed_objects[] = {"; @@ -2767,7 +2768,7 @@ sub ProcessStructObjectLen my $type = $struct->{type}; - return 0 if not $type eq "sai_object_id_t" and not $type eq "sai_attribute_t*"; + return 0 if not $type eq "sai_object_id_t" and not $type eq "sai_object_list_t" and not $type eq "sai_attribute_t*"; my @objects = @{ $struct->{objects} }; @@ -4172,13 +4173,13 @@ sub ProcessSingleNonObjectId # allowed entries on object structs - if (not $type =~ /^sai_(nat_entry_data|mac|object_id|vlan_id|ip_address|ip_prefix|acl_chain|label_id|ip6|uint8|uint16|uint32|u32_range|\w+_type)_t$/) + if (not $type =~ /^sai_(nat_entry_data|mac|object_id|object_list|vlan_id|ip_address|ip_prefix|acl_chain|label_id|ip6|uint8|uint16|uint32|u32_range|\w+_type)_t$/) { LogError "struct member $member type '$type' is not allowed on struct $structname"; next; } - next if not $type eq "sai_object_id_t"; + next if not $type eq "sai_object_id_t" and not $type eq "sai_object_list_t"; my $objects = ExtractObjectsFromDesc($structname, $member, $desc); @@ -5298,107 +5299,6 @@ sub CreateSourcePragmaPop WriteSource "#pragma GCC diagnostic pop"; } -sub NeedsTwoPassProcessing -{ - # - # Detect if XML files require two-pass processing based on their structure. - # - # In Doxygen 1.9.8+, the XML structure changed: - # - sai_*.xml files have empty enum/define sections (just sectiondef exists, no memberdefs) - # - group_*.xml files contain the actual enum definitions with enumvalues and defines - # - # In older Doxygen versions: - # - sai_*.xml files contain both defines and enums with enumvalues - # - group_*.xml files don't exist or aren't used - # - # Returns 1 if two-pass processing is needed (new structure): - # - First pass: process all defines from group_*.xml files - # - Second pass: process enums/typedefs/functions from group_*.xml and sai_*.xml files - # - # Returns 0 if single-pass processing is sufficient (old structure): - # - Process sai_*.xml files only - # - - my $sai_file = "$XMLDIR/sai_8h.xml"; - - my $saiacl_file = "$XMLDIR/saiacl_8h.xml"; - - return 1 if not -f $sai_file or not -f $saiacl_file; - - # - # Check sai_8h.xml for enumvalue with name="SAI_API_SWITCH" - # - - my $sai_ref = ReadXml $sai_file; - - return 1 if not defined $sai_ref->{compounddef}[0]; - - my @sai_sections = @{ $sai_ref->{compounddef}[0]->{sectiondef} }; - - my $has_enumvalue = 0; - - for my $section (@sai_sections) - { - next if not $section->{kind} eq "enum"; - - for my $memberdef (@{ $section->{memberdef} }) - { - next if not $memberdef->{kind} eq "enum"; - - if (defined $memberdef->{enumvalue}) - { - for my $enumvalue (@{ $memberdef->{enumvalue} }) - { - if (defined $enumvalue->{name} and defined $enumvalue->{name}[0] and $enumvalue->{name}[0] eq "SAI_API_SWITCH") - { - $has_enumvalue = 1; - - last; - } - } - } - } - - last if $has_enumvalue; - } - - # - # Check saiacl_8h.xml for memberdef kind="define" - # - - my $saiacl_ref = ReadXml $saiacl_file; - - return 1 if not defined $saiacl_ref->{compounddef}[0]; - - my @saiacl_sections = @{ $saiacl_ref->{compounddef}[0]->{sectiondef} }; - - my $has_define = 0; - - for my $section (@saiacl_sections) - { - next if not $section->{kind} eq "define"; - - for my $memberdef (@{ $section->{memberdef} }) - { - if ($memberdef->{kind} eq "define") - { - $has_define = 1; - - last; - } - } - - last if $has_define; - } - - # - # If sai_8h.xml has enumvalues and saiacl_8h.xml has defines, it's old structure (single-pass) - # Otherwise, use two-pass processing (group_*.xml files contain the actual content) - # - - return not ($has_enumvalue and $has_define); -} - sub ProcessXmlFiles { if (NeedsTwoPassProcessing()) @@ -5637,7 +5537,7 @@ sub ProcessNotificationStruct next if $type =~ /^(uint32_t|bool)$/; next if $type =~ /^(sai_twamp_session_stats_data_t)$/; - if ($type =~ /^(sai_object_id_t|sai_attribute_t\*)$/) + if ($type =~ /^(sai_object_id_t|sai_object_list_t|sai_attribute_t\*)$/) { my $objects = ExtractObjectsFromDesc($structname, $member, $desc); diff --git a/meta/saisanitycheck.c b/meta/saisanitycheck.c index ae1d23b22..d77218bb8 100644 --- a/meta/saisanitycheck.c +++ b/meta/saisanitycheck.c @@ -5554,6 +5554,18 @@ void check_graph_connected() continue; } + if (SAI_OBJECT_TYPE_PERFMON == idx2ot(i)) + { + /* + * Allow performance monitor object to be disconnected from main graph + * as use case is by querying base object stats and not by direct reference + */ + + META_LOG_WARN("perfmon object %s is disconnected from graph", + sai_metadata_all_object_type_infos[i]->objecttypename); + + continue; + } META_ASSERT_FAIL("object %s is disconnected from graph", sai_metadata_all_object_type_infos[i]->objecttypename); } diff --git a/meta/templates/sai_rpc_server_helper_functions.tt b/meta/templates/sai_rpc_server_helper_functions.tt index 7ea24b063..1ca1ad545 100644 --- a/meta/templates/sai_rpc_server_helper_functions.tt +++ b/meta/templates/sai_rpc_server_helper_functions.tt @@ -122,7 +122,7 @@ void sai_thrift_parse_[% struct.short_name %](const [% struct.thrift_name %] &th #ifdef UNSUPPORTED /* complex struct members are not supported yet */ [%- END -%] - [% struct.short_name %]->[% member.name %] = thrift_[% struct.short_name %].[% member.name %]; + [% struct.short_name %]->[% member.name %] = static_cast<[% member.type.name %]>(thrift_[% struct.short_name %].[% member.name %]); [%- IF member.type.thrift_name.match(unsupported_fields) -%] #endif diff --git a/meta/xmlutils.pm b/meta/xmlutils.pm index 2aa8c0f6a..de722b0ac 100644 --- a/meta/xmlutils.pm +++ b/meta/xmlutils.pm @@ -568,6 +568,109 @@ sub ExtractStructInfoEx return %Struct; } +sub NeedsTwoPassProcessing +{ + # + # Detect if XML files require two-pass processing based on their structure. + # + # In Doxygen 1.9.8+, the XML structure changed: + # - sai_*.xml files have empty enum/define sections (just sectiondef exists, no memberdefs) + # - group_*.xml files contain the actual enum definitions with enumvalues and defines + # + # In older Doxygen versions: + # - sai_*.xml files contain both defines and enums with enumvalues + # - group_*.xml files don't exist or aren't used + # + # Returns 1 if two-pass processing is needed (new structure): + # - First pass: process all defines from group_*.xml files + # - Second pass: process enums/typedefs/functions from group_*.xml and sai_*.xml files + # + # Returns 0 if single-pass processing is sufficient (old structure): + # - Process sai_*.xml files only + # + + my $XMLDIR = $main::XMLDIR; + + my $sai_file = "$XMLDIR/sai_8h.xml"; + + my $saiacl_file = "$XMLDIR/saiacl_8h.xml"; + + return 1 if not -f $sai_file or not -f $saiacl_file; + + # + # Check sai_8h.xml for enumvalue with name="SAI_API_SWITCH" + # + + my $sai_ref = ReadXml $sai_file; + + return 1 if not defined $sai_ref->{compounddef}[0]; + + my @sai_sections = @{ $sai_ref->{compounddef}[0]->{sectiondef} }; + + my $has_enumvalue = 0; + + for my $section (@sai_sections) + { + next if not $section->{kind} eq "enum"; + + for my $memberdef (@{ $section->{memberdef} }) + { + next if not $memberdef->{kind} eq "enum"; + + if (defined $memberdef->{enumvalue}) + { + for my $enumvalue (@{ $memberdef->{enumvalue} }) + { + if (defined $enumvalue->{name} and defined $enumvalue->{name}[0] and $enumvalue->{name}[0] eq "SAI_API_SWITCH") + { + $has_enumvalue = 1; + + last; + } + } + } + } + + last if $has_enumvalue; + } + + # + # Check saiacl_8h.xml for memberdef kind="define" + # + + my $saiacl_ref = ReadXml $saiacl_file; + + return 1 if not defined $saiacl_ref->{compounddef}[0]; + + my @saiacl_sections = @{ $saiacl_ref->{compounddef}[0]->{sectiondef} }; + + my $has_define = 0; + + for my $section (@saiacl_sections) + { + next if not $section->{kind} eq "define"; + + for my $memberdef (@{ $section->{memberdef} }) + { + if ($memberdef->{kind} eq "define") + { + $has_define = 1; + + last; + } + } + + last if $has_define; + } + + # + # If sai_8h.xml has enumvalues and saiacl_8h.xml has defines, it's old structure (single-pass) + # Otherwise, use two-pass processing (group_*.xml files contain the actual content) + # + + return not ($has_enumvalue and $has_define); +} + sub ExtractDescription { my ($type, $value, $item) = @_; @@ -636,6 +739,7 @@ BEGIN our @ISA = qw(Exporter); our @EXPORT = qw/ ReadXml UnescapeXml GetSaiXmlFiles GetXmlUnionFiles GetGroupXmlFiles + NeedsTwoPassProcessing ExtractDescription ExtractStructInfo ExtractStructInfoEx /; }