diff --git a/.gitignore b/.gitignore index 20dedcaee8..75769ddb2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # metadata build artifacts meta/xml meta/html +meta/temp meta/*.o meta/*.lo diff --git a/doc/ECMP/Class-Based-Forwarding.md b/doc/ECMP/Class-Based-Forwarding.md new file mode 100644 index 0000000000..5fca941dd3 --- /dev/null +++ b/doc/ECMP/Class-Based-Forwarding.md @@ -0,0 +1,211 @@ +# Class-based Forwarding + +Title | Class-based Forwarding +------------|---------------- +Authors | Cisco +Status | In review +Type | Standards track +Created | 04/14/2021 +SAI-Version | 1.8 + +Class-based forwarding provides a method to steer traffic among multiple paths through the network by policy rather than, or in combination with, traditional ECMP/UCMP flow-hashing. + +A new type of next-hop group is introduced: + +``` +typedef enum _sai_next_hop_group_type_t +{ +... + /** Next hop group is class-based, with members selected by Forwarding class */ + SAI_NEXT_HOP_GROUP_TYPE_CLASS_BASED, +... +} sai_next_hop_group_type_t; +``` + +The behavior of SAI_NEXT_HOP_GROUP_TYPE_CLASS_BASED differs from the traditional SAI_NEXT_HOP_GROUP_TYPE_ECMP, in that each packet will have a Forwarding class that chooses next-hop group member index. + +This is accomplished by directly mapping each forwarding class to the group member index, via map configured to the next-hop group object. + +``` + /** + * @brief Member selection map + * + * @type sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MAP + * @allownull true + * @default SAI_NULL_OBJECT_ID + * @validonly SAI_NEXT_HOP_GROUP_ATTR_TYPE == SAI_NEXT_HOP_GROUP_TYPE_CLASS_BASED + */ + SAI_NEXT_HOP_GROUP_ATTR_SELECTION_MAP, +``` + +If a packet arrives with a forwarding-class which is not present in the map, the chosen index shall be 0. + +``` +typedef enum _sai_next_hop_group_member_attr_t +{ +... + /** + * @brief Object index in the next-hop group. + * + * Index specifying the strict member's order. + * Allowed value range for is from 0 to SAI_NEXT_HOP_GROUP_ATTR_REAL_SIZE - 1. + * Should only be used if the type of owning group is SAI_NEXT_HOP_GROUP_TYPE_FINE_GRAIN_ECMP + * or SAI_NEXT_HOP_GROUP_TYPE_CLASS_BASED. + * + * @type sai_uint32_t + * @flags CREATE_ONLY + * @default 0 + */ + SAI_NEXT_HOP_GROUP_MEMBER_ATTR_INDEX, +... +} sai_next_hop_group_member_attr_t; +``` + +If the map selects an index for which a member does not exist, the packet shall be treated as having a next-hop of SAI_NULL_OBJECT_ID, dropping the packet. + +Members of type next-hop or next-hop groups of type ECMP shall be allowed. To allow this, next-hop group member type is extended to allow other next-hop groups: + +``` + /** + * @brief Next hop id + * + * @type sai_object_id_t + * @flags MANDATORY_ON_CREATE | CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_NEXT_HOP, SAI_OBJECT_TYPE_NEXT_HOP_GROUP + */ + SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID, +``` + +*Note: While this would also be a means to configure a hierarchical ECMP, hierarchical ECMP is outside the scope of this proposal.* + +The forwarding-class for a packet may be selected via qos-map or ACL. + +``` +typedef enum _sai_qos_map_type_t +{ +... + /** QOS Map to set DSCP to Forwarding class */ + SAI_QOS_MAP_TYPE_DSCP_TO_FORWARDING_CLASS = 0x0000000d, + + /** QOS Map to set EXP to Forwarding class */ + SAI_QOS_MAP_TYPE_MPLS_EXP_TO_FORWARDING_CLASS = 0x0000000e, +... +} sai_qos_map_type_t; +``` + +``` +typedef enum _sai_acl_entry_attr_t +... + /** + * @brief Set Forwarding Class + * + * @type sai_acl_action_data_t sai_uint8_t + * @flags CREATE_AND_SET + * @default disabled + */ + SAI_ACL_ENTRY_ATTR_ACTION_SET_FORWARDING_CLASS, +... +} sai_acl_entry_attr_t; +``` + +If the packet is not assigned a forwarding-class, then the forwarding-class of the packet shall be 0. For example, if no qos-map or ACL is configured. Normal QOS/ACL precedence rules apply; if supported by the implementation, the ACL would override the QOS MAP decision. + +*Resource monitoring considerations:* + +The attribute SAI_SWITCH_ATTR_MAX_NUMBER_OF_FORWARDING_CLASSES may be used to identify the maximum forwarding-class allowed. + +The SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MAP object is a resource. The sai_object_type_get_availability() API may be used to query the maximum number of permitted maps. + +*Class-based forwarding group configuration example:* +``` +/****************************************************** + * Create a forwarding-class -> index map. + * In this example, map 8 forwarding-classes to 2 members. + * FC 0-5 -> index 0 + * FC 6-7 -> index 1 + ******************************************************/ + const int num_forwarding_classes = 8; + const int num_members = 2; + + sai_object_id_t nh_group_map; + + sai_map_t fc_map[num_forwarding_classes]; + for (int fc = 0; fc < num_forwarding_classes; ++fc) { + fc_map[fc].key = fc; + if (fc >= 6) { + fc_map[fc].value = 1; + } else { + fc_map[fc].value = 0; + } + } + + sai_map_list_t fc_map_list; + fc_map_list.key.count = num_forwarding_classes; + fc_map_list.key.list = fc_map; + + attr.id = SAI_NEXT_HOP_GROUP_MAP_ATTR_TYPE; + attr.value.u32 = SAI_NEXT_HOP_GROUP_MAP_TYPE_FORWARDING_CLASS_TO_INDEX; + attrs.push_back(attr); + + attr.id = SAI_NEXT_HOP_GROUP_MAP_ATTR_MAP_TO_VALUE_LIST; + attr.value.maplist = fc_map_list; + attrs.push_back(attr); + + sai_next_hop_group_api->create_next_hop_group_map( + &nh_group_map, + g_switch_id, + attrs.size(), + attrs.data()); + + /***************************************************** + * Create a class-based forwarding group + *****************************************************/ + attrs.clear(); + + sai_object_id_t nh_group; + + attr.id = SAI_NEXT_HOP_GROUP_ATTR_TYPE; + attr.value.u32 = SAI_NEXT_HOP_GROUP_TYPE_CLASS_BASED; + attrs.push_back(attr); + + attr.id = SAI_NEXT_HOP_GROUP_ATTR_CONFIGURED_SIZE; + attr.value.u32 = num_members; + attrs.push_back(attr); + + attr.id = SAI_NEXT_HOP_GROUP_ATTR_SELECTION_MAP; + attr.value.oid = nh_group_map; + attrs.push_back(attr); + + sai_next_hop_group_api->create_next_hop_group( + &nh_group, + g_switch_id, + attrs.size(), + attrs.data()); + + /***************************************************** + * Create members + *****************************************************/ + attrs.clear(); + + for (index = 0; index < num_members; ++index) { + attr.id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID; + attr.value.oid = nh_group; + attrs.push_back(attr); + + attr.id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID; + attr.value.oid = destinations[index]; // Next-hop or ECMP group + attrs.push_back(attr); + + attr.id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_INDEX; + attr.value.u32 = index; + attrs.push_back(attr); + + sai_next_hop_group_api->create_next_hop_group_member( + &members[member_index], + g_switch_id, + attrs.size(), + attrs.data()); + } +``` diff --git a/doc/behavioral model/pipeline_v9.vsdx b/doc/behavioral model/pipeline_v9.vsdx new file mode 100644 index 0000000000..d10a59731a Binary files /dev/null and b/doc/behavioral model/pipeline_v9.vsdx differ diff --git a/inc/saiacl.h b/inc/saiacl.h index b998f61706..ece8e6c2d4 100644 --- a/inc/saiacl.h +++ b/inc/saiacl.h @@ -272,7 +272,10 @@ typedef enum _sai_acl_action_type_t SAI_ACL_ACTION_TYPE_SET_ECMP_HASH_ID = 0x00000030, /** Associate with virtual router */ - SAI_ACL_ACTION_TYPE_SET_VRF = 0x00000031 + SAI_ACL_ACTION_TYPE_SET_VRF = 0x00000031, + + /** Set Forwarding class */ + SAI_ACL_ACTION_TYPE_SET_FORWARDING_CLASS = 0x00000034, } sai_acl_action_type_t; @@ -2922,10 +2925,19 @@ typedef enum _sai_acl_entry_attr_t */ SAI_ACL_ENTRY_ATTR_ACTION_SET_VRF = SAI_ACL_ENTRY_ATTR_ACTION_START + 0x31, + /** + * @brief Set Forwarding Class + * + * @type sai_acl_action_data_t sai_uint8_t + * @flags CREATE_AND_SET + * @default disabled + */ + SAI_ACL_ENTRY_ATTR_ACTION_SET_FORWARDING_CLASS = SAI_ACL_ENTRY_ATTR_ACTION_START + 0x34, + /** * @brief End of Rule Actions */ - SAI_ACL_ENTRY_ATTR_ACTION_END = SAI_ACL_ENTRY_ATTR_ACTION_ADD_VLAN_PRI, + SAI_ACL_ENTRY_ATTR_ACTION_END = SAI_ACL_ENTRY_ATTR_ACTION_SET_FORWARDING_CLASS, /** * @brief End of ACL Entry attributes diff --git a/inc/sainexthopgroup.h b/inc/sainexthopgroup.h index 99c5a4aef2..a0782e3c30 100644 --- a/inc/sainexthopgroup.h +++ b/inc/sainexthopgroup.h @@ -53,6 +53,9 @@ typedef enum _sai_next_hop_group_type_t /** Next hop protection group. Contains primary and backup next hops. */ SAI_NEXT_HOP_GROUP_TYPE_PROTECTION, + /** Next hop group is class-based, with members selected by Forwarding class */ + SAI_NEXT_HOP_GROUP_TYPE_CLASS_BASED, + /* Other types of next hop group to be defined in the future, e.g., WCMP */ } sai_next_hop_group_type_t; @@ -151,7 +154,7 @@ typedef enum _sai_next_hop_group_attr_t * @type sai_uint32_t * @flags CREATE_ONLY * @default 0 - * @validonly SAI_NEXT_HOP_GROUP_ATTR_TYPE == SAI_NEXT_HOP_GROUP_TYPE_FINE_GRAIN_ECMP + * @validonly SAI_NEXT_HOP_GROUP_ATTR_TYPE == SAI_NEXT_HOP_GROUP_TYPE_FINE_GRAIN_ECMP or SAI_NEXT_HOP_GROUP_ATTR_TYPE == SAI_NEXT_HOP_GROUP_TYPE_CLASS_BASED * @isresourcetype true */ SAI_NEXT_HOP_GROUP_ATTR_CONFIGURED_SIZE, @@ -169,6 +172,18 @@ typedef enum _sai_next_hop_group_attr_t */ SAI_NEXT_HOP_GROUP_ATTR_REAL_SIZE, + /** + * @brief Next hop group selection map + * + * @type sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MAP + * @allownull true + * @default SAI_NULL_OBJECT_ID + * @validonly SAI_NEXT_HOP_GROUP_ATTR_TYPE == SAI_NEXT_HOP_GROUP_TYPE_CLASS_BASED + */ + SAI_NEXT_HOP_GROUP_ATTR_SELECTION_MAP, + /** * @brief End of attributes */ @@ -203,7 +218,7 @@ typedef enum _sai_next_hop_group_member_attr_t * * @type sai_object_id_t * @flags MANDATORY_ON_CREATE | CREATE_AND_SET - * @objects SAI_OBJECT_TYPE_NEXT_HOP + * @objects SAI_OBJECT_TYPE_NEXT_HOP, SAI_OBJECT_TYPE_NEXT_HOP_GROUP */ SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID, @@ -263,7 +278,8 @@ typedef enum _sai_next_hop_group_member_attr_t * * Index specifying the strict member's order. * Allowed value range for is from 0 to SAI_NEXT_HOP_GROUP_ATTR_REAL_SIZE - 1. - * Should only be used if the type of owning group is SAI_NEXT_HOP_GROUP_TYPE_FINE_GRAIN_ECMP. + * Should only be used if the type of owning group is SAI_NEXT_HOP_GROUP_TYPE_FINE_GRAIN_ECMP + * or SAI_NEXT_HOP_GROUP_TYPE_CLASS_BASED. * * @type sai_uint32_t * @flags CREATE_ONLY @@ -311,6 +327,50 @@ typedef enum _sai_next_hop_group_member_attr_t } sai_next_hop_group_member_attr_t; +typedef enum _sai_next_hop_group_map_type_t +{ + /** Next hop group map forwarding-class to index */ + SAI_NEXT_HOP_GROUP_MAP_TYPE_FORWARDING_CLASS_TO_INDEX + +} sai_next_hop_group_map_type_t; + +typedef enum _sai_next_hop_group_map_attr_t +{ + /** + * @brief Start of attributes + */ + SAI_NEXT_HOP_GROUP_MAP_ATTR_START, + + /** + * @brief Next hop group map type + * + * @type sai_next_hop_group_map_type_t + * @flags MANDATORY_ON_CREATE | CREATE_ONLY + */ + SAI_NEXT_HOP_GROUP_MAP_ATTR_TYPE = SAI_NEXT_HOP_GROUP_MAP_ATTR_START, + + /** + * @brief Next hop group entries associated with this map. + * + * @type sai_map_list_t + * @flags CREATE_AND_SET + * @default empty + */ + SAI_NEXT_HOP_GROUP_MAP_ATTR_MAP_TO_VALUE_LIST, + + /** + * @brief End of attributes + */ + SAI_NEXT_HOP_GROUP_MAP_ATTR_END, + + /** Custom range base value */ + SAI_NEXT_HOP_GROUP_MAP_ATTR_CUSTOM_RANGE_START = 0x10000000, + + /** End of custom range base */ + SAI_NEXT_HOP_GROUP_MAP_ATTR_CUSTOM_RANGE_END + +} sai_next_hop_group_map_attr_t; + /** * @brief Create next hop group * @@ -390,7 +450,7 @@ typedef sai_status_t (*sai_remove_next_hop_group_member_fn)( _In_ sai_object_id_t next_hop_group_member_id); /** - * @brief Set Next Hop Group attribute + * @brief Set Next Hop Group member attribute * * @param[in] next_hop_group_member_id Next hop group member ID * @param[in] attr Attribute @@ -402,7 +462,7 @@ typedef sai_status_t (*sai_set_next_hop_group_member_attribute_fn)( _In_ const sai_attribute_t *attr); /** - * @brief Get Next Hop Group attribute + * @brief Get Next Hop Group member attribute * * @param[in] next_hop_group_member_id Next hop group member ID * @param[in] attr_count Number of attributes @@ -415,6 +475,58 @@ typedef sai_status_t (*sai_get_next_hop_group_member_attribute_fn)( _In_ uint32_t attr_count, _Inout_ sai_attribute_t *attr_list); +/** + * @brief Create next hop group map + * + * @param[out] next_hop_group_map_id Next hop group map 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_next_hop_group_map_fn)( + _Out_ sai_object_id_t *next_hop_group_map_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + +/** + * @brief Remove next hop group map + * + * @param[in] next_hop_group_map_id Next hop group map ID + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_remove_next_hop_group_map_fn)( + _In_ sai_object_id_t next_hop_group_map_id); + +/** + * @brief Set Next Hop Group map attribute + * + * @param[in] next_hop_group_map_id Next hop group map ID + * @param[in] attr Attribute + * + * @return #SAI_STATUS_SUCCESS on success, failure status code on error + */ +typedef sai_status_t (*sai_set_next_hop_group_map_attribute_fn)( + _In_ sai_object_id_t next_hop_group_map_id, + _In_ const sai_attribute_t *attr); + +/** + * @brief Get next hop group map attribute + * + * @param[in] next_hop_group_map_id Next hop group map 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_next_hop_group_map_attribute_fn)( + _In_ sai_object_id_t next_hop_group_map_id, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list); + /** * @brief Next Hop methods table retrieved with sai_api_query() */ @@ -430,6 +542,10 @@ typedef struct _sai_next_hop_group_api_t sai_get_next_hop_group_member_attribute_fn get_next_hop_group_member_attribute; sai_bulk_object_create_fn create_next_hop_group_members; sai_bulk_object_remove_fn remove_next_hop_group_members; + sai_create_next_hop_group_map_fn create_next_hop_group_map; + sai_remove_next_hop_group_map_fn remove_next_hop_group_map; + sai_set_next_hop_group_map_attribute_fn set_next_hop_group_map_attribute; + sai_get_next_hop_group_map_attribute_fn get_next_hop_group_map_attribute; } sai_next_hop_group_api_t; /** diff --git a/inc/saiport.h b/inc/saiport.h index e3e0420c3c..cd0ee73317 100644 --- a/inc/saiport.h +++ b/inc/saiport.h @@ -2004,6 +2004,34 @@ typedef enum _sai_port_attr_t */ SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL_FORWARD, + /** + * @brief Enable DSCP -> Forwarding Class MAP on port + * + * Map id = #SAI_NULL_OBJECT_ID to disable map on port. + * Default no map. + * + * @type sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_QOS_MAP + * @allownull true + * @default SAI_NULL_OBJECT_ID + */ + SAI_PORT_ATTR_QOS_DSCP_TO_FORWARDING_CLASS_MAP, + + /** + * @brief Enable EXP -> Forwarding Class MAP on port + * + * Map id = #SAI_NULL_OBJECT_ID to disable map on port. + * Default no map. + * + * @type sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_QOS_MAP + * @allownull true + * @default SAI_NULL_OBJECT_ID + */ + SAI_PORT_ATTR_QOS_MPLS_EXP_TO_FORWARDING_CLASS_MAP, + /** * @brief End of attributes */ diff --git a/inc/saiqosmap.h b/inc/saiqosmap.h index d723f15468..5b5d867e4c 100644 --- a/inc/saiqosmap.h +++ b/inc/saiqosmap.h @@ -77,6 +77,12 @@ typedef enum _sai_qos_map_type_t /** QOS Map to set traffic class and color to EXP */ SAI_QOS_MAP_TYPE_TC_AND_COLOR_TO_MPLS_EXP = 0x0000000c, + /** QOS Map to set DSCP to Forwarding class */ + SAI_QOS_MAP_TYPE_DSCP_TO_FORWARDING_CLASS = 0x0000000d, + + /** QOS Map to set EXP to Forwarding class */ + SAI_QOS_MAP_TYPE_MPLS_EXP_TO_FORWARDING_CLASS = 0x0000000e, + /** Custom range base value */ SAI_QOS_MAP_TYPE_CUSTOM_RANGE_BASE = 0x10000000 diff --git a/inc/saiswitch.h b/inc/saiswitch.h index 362b838b7b..7d84985198 100644 --- a/inc/saiswitch.h +++ b/inc/saiswitch.h @@ -1590,7 +1590,7 @@ typedef enum _sai_switch_attr_t * @brief Enable DSCP -> TC MAP on switch. * * MAP id = #SAI_NULL_OBJECT_ID to disable map on switch. - * To enable/disable trust DSCP, Map ID should be added/removed on port. + * To enable/disable trust DSCP, Map ID should be added/removed on switch. * Default no map. * * @type sai_object_id_t @@ -2616,6 +2616,42 @@ typedef enum _sai_switch_attr_t */ SAI_SWITCH_ATTR_AVAILABLE_MY_MAC_ENTRIES, + /** + * @brief Maximum forwarding classes limit + * + * @type sai_uint8_t + * @flags READ_ONLY + */ + SAI_SWITCH_ATTR_MAX_NUMBER_OF_FORWARDING_CLASSES, + + /** + * @brief Enable DSCP -> Forwarding Class MAP on switch + * + * MAP id = #SAI_NULL_OBJECT_ID to disable map on switch. + * Default no map. + * + * @type sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_QOS_MAP + * @allownull true + * @default SAI_NULL_OBJECT_ID + */ + SAI_SWITCH_ATTR_QOS_DSCP_TO_FORWARDING_CLASS_MAP, + + /** + * @brief Enable EXP -> Forwarding Class MAP on switch + * + * MAP id = #SAI_NULL_OBJECT_ID to disable map on switch. + * Default no map. + * + * @type sai_object_id_t + * @flags CREATE_AND_SET + * @objects SAI_OBJECT_TYPE_QOS_MAP + * @allownull true + * @default SAI_NULL_OBJECT_ID + */ + SAI_SWITCH_ATTR_QOS_MPLS_EXP_TO_FORWARDING_CLASS_MAP, + /** * @brief End of attributes */ diff --git a/inc/saitypes.h b/inc/saitypes.h index 782a790b8a..beba5e3b44 100644 --- a/inc/saitypes.h +++ b/inc/saitypes.h @@ -280,6 +280,7 @@ typedef enum _sai_object_type_t SAI_OBJECT_TYPE_SWITCH_TUNNEL = 95, SAI_OBJECT_TYPE_MY_SID_ENTRY = 96, SAI_OBJECT_TYPE_MY_MAC = 97, + SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MAP = 98, SAI_OBJECT_TYPE_MAX, /* Must remain in last position */ } sai_object_type_t; @@ -653,6 +654,7 @@ typedef enum _sai_packet_color_t * dot1p/DSCP/MPLS_EXP --> TC * dot1p/DSCP/MPLS_EXP --> Color * dot1p/DSCP/MPLS_EXP --> TC + Color + * DSCP/MPLS_EXP --> FC * TC --> dot1p/DSCP/MPLS_EXP. * TC + color --> dot1p/DSCP/MPLS_EXP. * TC --> Egress Queue. @@ -686,6 +688,9 @@ typedef struct _sai_qos_map_params_t /** MPLS exp value */ sai_uint8_t mpls_exp; + /** Forwarding class */ + sai_uint8_t fc; + } sai_qos_map_params_t; typedef struct _sai_qos_map_t diff --git a/meta/Makefile b/meta/Makefile index 4204d2c397..e71b990147 100644 --- a/meta/Makefile +++ b/meta/Makefile @@ -80,6 +80,8 @@ SYMBOLS = $(OBJ:=.symbols) all: toolsversions saisanitycheck saimetadatatest saiserializetest saidepgraph.svg $(SYMBOLS) ./checkheaders.pl ../inc ../inc + ./checkenumlock.sh + ./checkancestry.sh ./saimetadatatest >/dev/null ./saiserializetest >/dev/null ./saisanitycheck @@ -136,7 +138,7 @@ saidepgraph.svg: saidepgraph.gv .PHONY: clean clean: - rm -f *.o *~ .*~ *.tmp .*.swp .*.swo *.bak sai*.gv sai*.svg *.o.symbols + rm -f *.o *~ .*~ *.tmp .*.swp .*.swo *.bak sai*.gv sai*.svg *.o.symbols doxygen*.db rm -f saimetadata.h saimetadata.c saimetadatatest.c rm -f saisanitycheck saimetadatatest saiserializetest saidepgraphgen - rm -rf xml html dist + rm -rf xml html dist temp diff --git a/meta/ancestry.pl b/meta/ancestry.pl new file mode 100755 index 0000000000..95babbc1e0 --- /dev/null +++ b/meta/ancestry.pl @@ -0,0 +1,243 @@ +#!/usr/bin/perl +# +# Copyright (c) 2021 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 ancestry.pl +# +# @brief This module defines enum ancestry check for SAI headers +# + +BEGIN { push @INC,'.'; } + +use strict; +use warnings; +use diagnostics; +use sort 'stable'; # for enum initializers sort + +use Getopt::Std; +use Data::Dumper; +use utils; + +my %options = (); + +getopts("dsASl", \%options); + +our $optionPrintDebug = 1 if defined $options{d}; +our $optionDisableAspell = 1 if defined $options{A}; +our $optionUseXmlSimple = 1 if defined $options{s}; +our $optionDisableStyleCheck = 1 if defined $options{S}; +our $optionShowLogCaller = 1 if defined $options{l}; + +$SIG{__DIE__} = sub +{ + LogError "FATAL ERROR === MUST FIX === : @_"; + exit 1; +}; + +our $INCLUDE_DIR = "temp"; +our %SAI_ENUMS = (); +our %SAI_DEFINES = (); +our %HISTORY = (); +our %IGNORED = (); + +sub ProcessSingleHeader +{ + my $header = shift; + + my $data = ReadHeaderFile $header; + + my @lines = split/\n/,$data; + + my $currentEnum = "undefined"; + my $currentEnumPrefix = "undefined"; + + my $ignore = ""; + + for my $line (@lines) + { + if ($line =~ /#define\s+(SAI_\w+)\s+(\(?".*"|$NUMBER_REGEX\)?)$/) + { + LogDebug "Defined $1 = $2"; + + $SAI_DEFINES{$1} = $2; + next; + } + + if ($line =~ /^\s*typedef\s+enum\s+_((sai_\w+_)t)/) + { + $currentEnum = $1; + $currentEnumPrefix = uc($2); + + my @values = (); + my @inits = (); + + $SAI_ENUMS{$currentEnum}->{values} = \@values; + $SAI_ENUMS{$currentEnum}->{inits} = \@inits; + + LogDebug "enum found $currentEnum"; + next; + } + + $ignore = "ignore" if $line =~ /\@ignore/; + + if ($line =~ /^\s*(${currentEnumPrefix}\w+)(.*)$/) + { + my $enumName = $1; + my $init = (defined $2) ? $2 : ""; + + $init =~ s!\s*/\*.*\*/!!; # remove potential comment + $init =~ s/^\s*=\s*/= /; # remove assigner + $init =~ s/\s*,\s*$//; # remove comma + + push @{ $SAI_ENUMS{$currentEnum}->{values} }, $enumName; + push @{ $SAI_ENUMS{$currentEnum}->{inits} }, $init; + + $IGNORED{$enumName} = $init if $ignore ne ""; + + $ignore = ""; + } + } +} + +sub ProcessHeaders +{ + my $commit = shift; + + my @headers = GetHeaderFiles("temp/commit-$commit/inc"); + + for my $header (@headers) + { + LogDebug "Processing $header"; + + ProcessSingleHeader "temp/commit-$commit/inc/$header"; + } +} + +sub ProcessAllEnumInitializers +{ + for my $enumTypeName (sort keys %SAI_ENUMS) + { + LogDebug $enumTypeName; + + my $arr_ref = $SAI_ENUMS{$enumTypeName}->{values}; + my $ini_ref = $SAI_ENUMS{$enumTypeName}->{inits}; + + ProcessEnumInitializers($arr_ref, $ini_ref, $enumTypeName, \%SAI_DEFINES); + } +} + +sub BuildCommitHistory +{ + my $commit = shift; + + for my $enumTypeName (sort keys %SAI_ENUMS) + { + LogDebug $enumTypeName; + + my $arr_ref = $SAI_ENUMS{$enumTypeName}->{values}; + my $ini_ref = $SAI_ENUMS{$enumTypeName}->{inits}; + + my $count = scalar @$arr_ref; + + for (my $idx = 0; $idx < $count; $idx++) + { + my $enumName = $arr_ref->[$idx]; + my $enumValue = $ini_ref->[$idx]; + + # CheckAllEnumsEndings make sure _START match _END + + next if $enumName =~ /_START$/; + next if $enumName =~ /_END$/; + next if $enumName =~ /_RANGE_BASE$/; + + next if $enumName eq "SAI_API_MAX"; + next if $enumName eq "SAI_OBJECT_TYPE_MAX"; + + LogError "wrong initializer on $enumName $enumValue" if not $enumValue =~ /^0x[0-9a-f]{8}$/; + + if (defined $HISTORY{$enumTypeName}{$enumName} and $HISTORY{$enumTypeName}{$enumName}{value} eq $enumValue) + { + # ok, value is the same + } + elsif (not defined $HISTORY{$enumTypeName} or not defined $HISTORY{$enumTypeName}{$enumName}) + { + $HISTORY{$enumTypeName}{$enumName}{name} = $enumName; + $HISTORY{$enumTypeName}{$enumName}{value} = $enumValue; + $HISTORY{$enumTypeName}{$enumName}{commit} = $commit; + + if (not defined $HISTORY{$enumTypeName}{$enumValue}) + { + $HISTORY{$enumTypeName}{$enumValue} = $enumName; + } + elsif ($HISTORY{$enumTypeName}{$enumValue} eq $enumName) + { + # ok this is the same enum in history + } + elsif (defined $IGNORED{$enumName}) + { + # ok, values are the sam, but enum is ignored (left for backward compatibility) + # but we don't check if ignored value changed, it potentially switch to different ignore + } + else # 2 enums have same integer value + { + #print "elsif (defined $enumName $IGNORED{$enumName} and $IGNORED{$enumName} eq $HISTORY{$enumTypeName}{$enumName}{name})"; + + LogWarning "Both enums have the same value $enumName and $HISTORY{$enumTypeName}{$enumValue} = $enumValue"; + } + } + else + { + LogError "check ! $enumName value is $enumValue, but on was $HISTORY{$enumTypeName}{$enumName}{value} on commit $HISTORY{$enumTypeName}{$enumName}{commit}"; + + $HISTORY{$enumTypeName}{$enumName}{value} = $enumValue; + $HISTORY{$enumTypeName}{$enumName}{commit} = $commit; + } + } + } +} + +sub CleanData +{ + %SAI_ENUMS = (); + %SAI_DEFINES = (); + %IGNORED = (); +} + +# +# MAIN +# + +for my $commit (@ARGV) +{ + # reset + + LogInfo "processing commit $commit"; + + CleanData(); + + ProcessHeaders $commit; + + ProcessAllEnumInitializers(); + + # print Dumper \%SAI_ENUMS; + + BuildCommitHistory $commit; +} + +ExitOnErrorsOrWarnings(); diff --git a/meta/checkancestry.sh b/meta/checkancestry.sh new file mode 100755 index 0000000000..d4b8443a62 --- /dev/null +++ b/meta/checkancestry.sh @@ -0,0 +1,115 @@ +#!/bin/bash +# +# Copyright (c) 2021 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 checkancestry.sh +# +# @brief This module defines ancestry script +# + + +# To list git ancestry all comitts (even if there is a tree not single line) +# this can be usefull to build histroy of enums from root (enum lock) to the +# current origin/master and current commit - and it will be possible to fix +# mistakes. + +# examples below are to show how to get correct git history tree +# git log --graph --oneline --ancestry-path c388490^..0b90765 | cat +# git rev-list --ancestry-path c388490^..0b90765 + +# If we will have our base commit, we will assume that each previous commit +# followed metadata check, and then we can use naive approach for parsing enum +# values instead of doing gcc compile whch can take long time. With this +# approach we should be able to build entire history from base commit throug +# all commits up to the current PR. This will sure that there will be no +# abnormalities if some enums will be removed and then added again with +# different value. This will also help to track the issue if two PRs will pass +# validation but after they will be merged they could potentially cause enum +# value issue and this approach will catch that. +# +# Working throug 25 commits takes about 0.4 seconds + parsing so it seems like +# not a hudge time to make sure all commits are safe and even if we get at some +# point that this will be "too slow", having all history, we can sometimes +# produce "known" history with enum values and keep that file as a reference +# and load it at begin, and start checking commits from one of the future +# commits, basicially reducing processing time to zero. + +# Just for sanity we can also keep headers check to 1 commit back and alse +# maybe we can add one gcc check current to history, + +set -e + +# 1. get all necessary data to temp directory for future processing +# 2. pass all interesting commits to processor to build history + +function clean_temp_dir() +{ + rm -rf temp +} + +function create_temp_dir() +{ + mkdir temp +} + +function checkout_inc_directories() +{ + echo "git checkout work tree commits:" $LIST + + for commit in $LIST + do + #echo working on commit $commit + + mkdir temp/commit-$commit + mkdir temp/commit-$commit/inc + + git --work-tree=temp/commit-$commit checkout $commit inc 2>/dev/null + + done +} + +function create_commit_list() +{ + local begin=$1 + local end=$2 + + echo "git rev list from $begin to $end" + + LIST=$(git rev-list --ancestry-path ${begin}^..${end} | xargs -n 1 git rev-parse --short | tac) +} + +function check_enum_history() +{ + perl ancestry.pl $LIST +} + +# +# MAIN +# + +# BEGIN_COMMIT is the commit from we want enums to be backward compatible + +BEGIN_COMMIT=3132018 +END_COMMIT=HEAD + +clean_temp_dir +create_temp_dir +create_commit_list $BEGIN_COMMIT $END_COMMIT +checkout_inc_directories +check_enum_history diff --git a/meta/checkenumlock.sh b/meta/checkenumlock.sh new file mode 100755 index 0000000000..f143b36094 --- /dev/null +++ b/meta/checkenumlock.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# +# 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 checkenumlock.sh +# +# @brief This module defines SAI enum values integration check for 2 header directories +# + +set -e + +rm -rf temp + +mkdir temp + +git --work-tree=temp/ checkout origin/master inc + +echo "Checking for possible enum values shift (current branch vs origin/master) ..." + +./checkheaders.pl -s ../inc/ temp/inc/ + +rm -rf temp diff --git a/meta/checkheaders.pl b/meta/checkheaders.pl index e7ce93f3e1..592f68733e 100755 --- a/meta/checkheaders.pl +++ b/meta/checkheaders.pl @@ -31,6 +31,12 @@ use File::Temp qw/ tempfile /; use Data::Dumper; +use Getopt::Std; + +my %options = (); +getopts("s", \%options); + +my $optionSkipSingleDefined = 1 if defined $options{s}; use utils; @@ -176,9 +182,30 @@ sub CheckHash for my $key (sort keys %A) { + if (defined $optionSkipSingleDefined) + { + # ignore attributes end, since those will shift + next if $key =~ /^SAI_\w+_ATTR_END$/; + + next if $key eq "SAI_ACL_TABLE_ATTR_FIELD_END"; + next if $key eq "SAI_ACL_ENTRY_ATTR_FIELD_END"; + next if $key eq "SAI_ACL_ENTRY_ATTR_ACTION_END"; + next if $key eq "SAI_OBJECT_TYPE_MAX"; + next if $key eq "SAI_API_MAX"; + + # NOTE: some other attributes/enum with END range could be added + } + if (not defined $B{$key}) { - LogError "enum $key only defined in $A{$key}{path}:$A{$key}{nr}"; + if (not defined $optionSkipSingleDefined) + { + LogError "enum $key only defined in $A{$key}{path}:$A{$key}{nr}" + } + else + { + LogInfo "enum $key only defined in $A{$key}{path}:$A{$key}{nr}" + } next; } diff --git a/meta/parse.pl b/meta/parse.pl index c9a435309b..0c3d331dda 100755 --- a/meta/parse.pl +++ b/meta/parse.pl @@ -521,162 +521,6 @@ sub ProcessDefineSection } } -sub ProcessEnumInitializers -{ - # - # This function attempts to figure out enum integers values during paring - # time in similar way as C compiler would do. Because SAI community agreed - # that enum grouping is more beneficial then ordering enums, then enum - # values could be not sorted any more. But if we figure out integers - # values, we could perform stable sort at this parser level, and generate - # enums metadata where enum values are sorted. - # - - my ($arr_ref, $ini_ref, $enumTypeName) = @_; - - return if $enumTypeName =~ /_extensions_t$/; # ignore initializers on extensions - - if (scalar(@$arr_ref) != scalar(@$ini_ref)) - { - LogError "attr array not matching initializers array on $enumTypeName"; - return; - } - - return if grep (/< 0, "should be positive"); @@ -5001,14 +5001,14 @@ void check_acl_user_defined_field() void check_label_size() { - SAI_META_LOG_ENTER(); + META_LOG_ENTER(); META_ASSERT_TRUE(sizeof(sai_label_id_t) == sizeof(uint32_t), "label is expected to be 32 bit"); } void check_switch_notify_list() { - SAI_META_LOG_ENTER(); + META_LOG_ENTER(); size_t i; @@ -5024,7 +5024,7 @@ void check_switch_notify_list() void check_switch_pointers_list() { - SAI_META_LOG_ENTER(); + META_LOG_ENTER(); size_t i; @@ -5040,7 +5040,7 @@ void check_switch_pointers_list() void check_defines() { - SAI_META_LOG_ENTER(); + META_LOG_ENTER(); /* * Check if defines are equal to their static values. @@ -5052,7 +5052,7 @@ void check_defines() void check_object_type_attributes() { - SAI_META_LOG_ENTER(); + META_LOG_ENTER(); size_t i = 0; @@ -5220,9 +5220,15 @@ void check_sai_version() /* SAI_VERSION uses 100 base for each component, so each define must not exceed this value */ - META_ASSERT_TRUE((SAI_MAJOR) >= 0 && (SAI_MAJOR) < 100, "invalid SAI_MAJOR version: %d", (SAI_MAJOR)); - META_ASSERT_TRUE((SAI_MINOR) >= 0 && (SAI_MINOR) < 100, "invalid SAI_MINOR version: %d", (SAI_MINOR)); - META_ASSERT_TRUE((SAI_REVISION) >= 0 && (SAI_REVISION) < 100, "invalid SAI_REVISION version: %d", (SAI_REVISION)); + /* Make sure sai version components are assignable to uint32_t */ + + uint32_t major = SAI_MAJOR; + uint32_t minor = SAI_MINOR; + uint32_t revision = SAI_REVISION; + + META_ASSERT_TRUE((major) < 100, "invalid SAI_MAJOR version: %d", (SAI_MAJOR)); + META_ASSERT_TRUE((minor) < 100, "invalid SAI_MINOR version: %d", (SAI_MINOR)); + META_ASSERT_TRUE((revision) < 100, "invalid SAI_REVISION version: %d", (SAI_REVISION)); } void check_max_conditions_len() diff --git a/meta/utils.pm b/meta/utils.pm index 466f2d34f3..df412dd47e 100644 --- a/meta/utils.pm +++ b/meta/utils.pm @@ -207,7 +207,7 @@ sub GetMetadataSourceFiles my @sources; - push @sources, GetFilesByRegex($dir, '^\w+\.(pm|pl|h|cpp|c)$'); + push @sources, GetFilesByRegex($dir, '^\w+\.(pm|pl|h|cpp|c|sh)$'); push @sources, GetFilesByRegex($dir, '^Makefile$'); return @sources; @@ -373,6 +373,178 @@ sub ExitOnErrors exit 1; } +sub ExitOnErrorsOrWarnings +{ + return if $errors == 0 and $warnings == 0; + + LogError "please corret all $errors error(s) and all $warnings warnings before continue"; + + exit 1; +} + +sub ProcessEnumInitializers +{ + # + # This function attempts to figure out enum integers values during paring + # time in similar way as C compiler would do. Because SAI community agreed + # that enum grouping is more beneficial then ordering enums, then enum + # values could be not sorted any more. But if we figure out integers + # values, we could perform stable sort at this parser level, and generate + # enums metadata where enum values are sorted. + # + + my ($arr_ref, $ini_ref, $enumTypeName, $SAI_DEFINES_REF) = @_; + + return if $enumTypeName =~ /_extensions_t$/; # ignore initializers on extensions + + if (scalar(@$arr_ref) != scalar(@$ini_ref)) + { + LogError "attr array not matching initializers array on $enumTypeName"; + return; + } + + #return if grep (/<{$2}; + + if (not defined $val) + { + LogError "Value $2 not defined using #define directive"; + } + elsif (not $val =~ /^0x[0-9a-f]+$/i) + { + LogError "$val not in hex format 0xYY"; + } + else + { + for my $i (0..$idx) + { + if ($$arr_ref[$i] eq $first) + { + $ini = sprintf("0x%08x", hex(@$ini_ref[$i]) + hex($val)); + + $previousEnumValue = hex($ini); + last; + } + } + + LogError "initializer $ini not found on $enumTypeName before $$arr_ref[$idx]" if not $ini =~ /^0x/; + } + } + elsif ($ini =~/^= (SAI_\w+) \+ (0x[0-9a-f]{1,8})$/) + { + my $first = $1; + my $val = $2; + + for my $i (0..$idx) + { + if ($$arr_ref[$i] eq $first) + { + $ini = sprintf("0x%08x", hex(@$ini_ref[$i]) + hex($val)); + + $previousEnumValue = hex($ini); + last; + } + } + + LogError "initializer $ini not found on $enumTypeName before $$arr_ref[$idx]" if not $ini =~ /^0x/; + } + elsif ($ini =~ /^= \(?(\d+) << (\d+)\)?$/) + { + $previousEnumValue = $1 << $2; + + $ini = sprintf("0x%08x", $previousEnumValue); + } + else + { + LogError "not supported initializer '$ini' on $$arr_ref[$idx], FIXME"; + } + + $idx++; + } + + # in final form all initializers must be hex numbers 8 digits long, since + # they will be used in stable sort + + if (scalar(grep (/^0x[0-9a-f]{8}$/, @$ini_ref)) != scalar(@$ini_ref)) + { + LogError "wrong initializers on $enumTypeName: @$ini_ref"; + return; + } + + my $before = "@$arr_ref"; + + my @joined = (); + + for my $idx (0..$#$arr_ref) + { + push @joined, "$$ini_ref[$idx]$$arr_ref[$idx]"; # format is: 0x00000000SAI_ + } + + my @sorted = sort { substr($a, 0, 10) cmp substr($b, 0, 10) } @joined; + + s/^0x[0-9a-f]{8}SAI/SAI/i for @sorted; + + my $after = "@sorted"; + + return if $after eq $before; + + LogDebug "Need sort initalizers for $enumTypeName"; + + @$arr_ref = (); + + push @$arr_ref, @sorted; +} + + BEGIN { our @ISA = qw(Exporter); @@ -380,7 +552,7 @@ BEGIN LogDebug LogInfo LogWarning LogError WriteFile GetHeaderFiles GetMetaHeaderFiles GetExperimentalHeaderFiles GetMetadataSourceFiles ReadHeaderFile GetNonObjectIdStructNames IsSpecialObject GetStructLists GetStructKeysInOrder - Trim ExitOnErrors + Trim ExitOnErrors ExitOnErrorsOrWarnings ProcessEnumInitializers WriteHeader WriteSource WriteTest WriteSwig WriteMetaDataFiles WriteSectionComment $errors $warnings $NUMBER_REGEX $HEADER_CONTENT $SOURCE_CONTENT $TEST_CONTENT diff --git a/test/saithrift/Makefile b/test/saithrift/Makefile index 57a5e8193b..cbe6f4823f 100644 --- a/test/saithrift/Makefile +++ b/test/saithrift/Makefile @@ -35,7 +35,7 @@ CTYPESGEN = /usr/local/bin/ctypesgen.py endif LIBS = -lthrift -lpthread ifeq ($(platform),vs) -LIBS += -lsaivs -lsaimeta -lsaimetadata +LIBS += -lsaivs -lsaimeta -lsaimetadata -lzmq else LIBS += -lsai endif