BCM芯片FP原理及相关SDK数据结构介绍

作者: 北京—小武

邮箱:night_elf1020@163.com

新浪微博:北京-小武

BCM芯片有几个大的模块: VLAN、L2、L3和FP等几个,其中FP的使用也最为灵活,能解析匹配数据包文的前128字节比特级的内容,动作包括转发、丢弃、结合qos修改相应字段、分配vid、流镜像、流重定向、指定端口转发(比如CPU口)、指定下一跳转发往、指定隧道转发等,往往在实现功能上有意想不到的功效。简单来说,如果硬件和BSP分别是九阳真经和九阴真经的话,那么port和vlan是少林七十二项绝技的组合,L2转发则是显得有点悠闲的峨眉派功夫,当然L3则是以太极拳为代表的武当派功夫,那么FP可以是以乾坤大挪移、吸星大法等为代表的魔教的邪而又邪的”旁门左道”,当然其他功能是零零散散的其他门派功夫。能够灵活运用好FP是增加很多交换机新功能的一种常用的手段。本文总结下FP这个模块BCM在硬件上的实现原理及SDK的相关数据结构。因为FP在实现功能上的灵活性,在此希望能抛砖引玉,激发大家更多的应用FP实现新功能的火花。

**BCM芯片FP实现原理
**

FP的全称是Fields Processors,也称为ContentAwareProcess(CAP),在BCM较早的芯片称为Fast Filter Processors(FFP),和现在的FP相比有一些原理不同,不过现在交换芯片已经不再使用FFP,所以在此也不再介绍。FP本质来说,是一组相互之间有关联的表,一起通过查找、匹配等来决定对报文施加的动作;在BCM芯片交换机中,有三种查找查找方式:hash,index,tcam。FP的查找主要用到了index和tcam,其中CAM的全称是ContentAddressable Memory,中文是内容寻址器,TCAM则是Ternary ContentAddressable Memory,中文称为三态内存寻址器,TCAM的实现是通过对应比特位+掩码产生三种匹配方式:掩码为0表示不关心、掩码为1且对应位为1或掩码为1且对应位为0。 这就是三态的具体含义。

在我们自研交换机所用的芯片中有三个FP:VFP(VLAN FP)、IFP(ingres FP)和EFP(Egress FP),另外在四代芯片kylin卡中曾出现外扩FP,称为E-IFP,表项大小为128K,为L2和L3转发用,有点openflow的意味。其中VFP主要用于对报文tag的处理,比如添删或修改vid的灵活QINQ的实现就基于此FP;IFP的用途比较多,主要是对进入端口后的报文进行处理,主要有入口acl、流重定向、流镜像、设置下一跳、为qos数据报文分类等用途;EFP的用途和IFP类似,但是因为EFP是报文在转出前在出端口进行处理的规则,IFP有的动作类型在EFP不太适用。虽然三种FP用途和数据包流经顺序不太一样,但是硬件原理是一致的。下面介绍下FP的硬件原理。

 

图1 FP原理组成图

图1中,每一个查找引擎和策略引擎及后面的counter资源和meter资源组合成一个规则组,芯片称之为一个slice,从图1可以看出,FP的实现有五部分组成:

智能解析模板:主要将报文信息(最多报文前128字节,可以精确到每一位bit)根据对每个slice的care字段将各对应字段解析出来,再加上前面L2、L3的转发信息,一起送给每个slice的查找引擎去匹配;

查找引擎:将解析出来的字段按照TCAM方式去查找本slice的规则是否有匹配的,即HIT的,只要有一条hit的即刻返回这条规则的index不再继续查找本slice后面规则,后面即使还有匹配的规则;这样做就是为了保证一个slice内部规则的优先级;如果没有匹配说明此slice没有匹配的规则或根本就没有规则,后面的流程也无需再走;

策略引擎:根据查找引擎得到的index直接索引策略引擎的动作,动作类型有转发、丢弃、重定向(包括到CPU口且可指定队列)、流镜像(包括到CPU口且可指定队列)、修改报文特定的字段(COS、DSCP、EXP等)、与后面的meter一起对报文染色并对不同染色报文指定相应动作、指定下一跳、指定ECMP、指定TTL是否修改、指定URPF的模式等相关动作;需要说明的是,一条规可以对报文执行多种动作,当然需要报文动作之间是不冲突的,即slice规则的动作冲突是靠配置下发来检查的,同一条规则有冲突的动作无法下发硬件;

Counter和meter资源: counter资源用于计数,有基于byte和packet的两种方式;meter主要用于测速,然后根据速度对报文进行染色(绿、黄和红)然后对报文应用不同的QOS策略;meter的工作原理可以参见我原先写的有关令牌桶相关文档。

动作冲突决策引擎:前面说过,一条slice的动作冲突是靠配置下发检查来实现的,冲突的动作无法同时下发到硬件;但是FP通常有多个slice,每个slice都有规则被匹配且动作时间有冲突时,需要动作冲突决策引擎来处理到底执行哪一个规则的动作,如果多个动作不冲突都执行;原则是丢弃、重定向等优先级最高,其他时候看slice号(这个slice号有的芯片只支持是物理的,高级芯片支持虚拟slice号),slice号越大优先级越高;

我们一条规则的匹配报文长度信息是有限的,对于IPV4报文同时匹配SMAC、DMAC、SIP、DIP等信息的时候,就不够了,芯片提供了将两条规则合并成一条规则,组成更大长度规则的方法,主要有图2示的两种,:

    

                      图2 两种slice宽模式

第一种是将一条slice的规则分为前后两部分,然后进行如图2左边的方式拼成double模式,这种模式称为double wide模式;第二种是用两条slice,直接如图2右边所示的方式拼成double模式,这种拼接方式称之为slice-paring模式。这两种模式,有的低级芯片都不支持,只能用单倍模式,有的芯片支持其中一种,我们的redstone交换机就只支持左边的这种方式。还有的芯片可以同时支持这两种拼接方式,那么就可以利用这点拼接处具有更大长度信息的四倍模式:

 

                       图3 四倍key模式

这种模式常用于IPV6报文的匹配中,因为IPV6的SIP和DIP实在太长了,再加上匹配其他信息,只能用四倍模式才能完全覆盖所有字段。但是我们的redstone交换机只支持slice-pairng模式,所以在IPV6报文的匹配中需要做折中。

前面我们提到slice有物理slice和虚拟slice,这个与物理内存和虚拟内存有点类似,FP都有物理slice,在高级的芯片上,为了更好的解决slice之间的动作冲突,对slice进行了虚拟编号,虚拟slice号越大优先级越高,这样就可以实现动作的优先级指定;可能做过物理slice的同学能体会为了保证各种应用slice的优先级在软件处理所做的代码处理工作有多么的艰辛;硬件进步这么一步,支持虚拟slice后,这部分工作就完全交给硬件来处理了,我们只需要指定优先级高低就可以了。而且虚拟slice还支持虚拟slice组的概念,每一个虚拟slice组就像一条slice一样,只会有一个动作产生出,这样就又大大减少了动作冲突的机会,而且还能使得每种应用使用更多的slice资源,无需考虑因为物理slice带来的动作优先级打破应用的优先级,更符合实际。

**BCM对FP操作的接口
**

BCM的SDK提供了四套对于FP资源使用和管理的函数接口,需要视具体应用环境和个人喜好来定夺,四种接口如下:

SOC API:直接硬件表项或寄存器操作,BCM各种问题明确不提倡的接口,因为需要配置人员管理和组织大量的逻辑;

Bcmx接口:通常不被使用的接口,因为不太灵活,且SDK被改造成为所有ACL规则为一个大的group,现在暂时IFP只有协议规则和ACL使用,所以还勉强满足需求,以lport作为端口的配置参数;但是每次下发新规则都要先删除原来规则,这个是没有必要的;这套接口和下面BCM接口的区别不是很大。相关函数接口有:

bcmx_field_group_create

bcmx_field_group_create_id

bcmx_field_group_compress

bcmx_field_group_install

bcmx_field_group_remove

bcmx_field_group_destroy

bcmx_field_entry_create

bcmx_field_entry_destroy

bcmx_field_entry_destroy_all

bcmx_field_data_qualifier_destroy

bcmx_field_data_qualifier_destroy_all

bcmx_field_qualify_clear

bcmx_field_data_qualifier_**_add

bcmx_field_data_qualifier_**_ delete等。

Bcm接口:BCM中对FP操作的最灵活的一组接口,非常适合运营商多种应用的场合,这组接口传递的参数也非常详细;相关函数接口有:

bcm_field_group_create

bcm_field_group_create_id

bcm_field_group_priority_set

bcm_field_group_compress

bcm_field_group_install

bcm_field_group_remove

bcm_field_group_destroy

bcm_field_entry_create

bcm_field_entry_create_id

bcm_field_entry_destroy

bcm_field_entry_destroy_all

bcm_field_entry_reinstall

bcm_field_entry_remove

bcm_field_qualify_clea

bcm_field_qualify_****

bcm_field_action_add

bcm_field_action_delete等。

Bcma接口:这套接口称为AdvancedContentAware Enhanced Software (ACES) implementation,传递的参数为bcma_acl_t*list,以结构体形式将规则所有参数下发到硬件;

/* List Management functions */

extern int bcma_acl_add(bcma_acl_t*list_id);

extern int bcma_acl_remove(bcma_acl_list_id_tlist_id);

extern int bcma_acl_get(bcma_acl_list_id_tlist_id, bcma_acl_t *list);

extern intbcma_acl_rule_add(bcma_acl_list_id_t list_id,

                             bcma_acl_rule_t*rule);

extern int bcma_acl_rule_remove(bcma_acl_list_id_tlist_id,

                               bcma_acl_rule_id_t rule_id);

extern intbcma_acl_rule_get(bcma_acl_rule_id_t rule_id,

                     
       bcma_acl_rule_t **rule);

/* Validation and Installation functions */

extern int bcma_acl_install(void);

extern int bcma_acl_uninstall(void); 等。

**SDK对FP资源管理的相关数据结构
**

1.      BCM芯片每一个unit都有这么一个结构体来保存芯片所有FP的资源占用情况:

static _field_control_t         *_field_control[BCM_MAX_NUM_UNITS];

field_control_t的具体内容为(每个变量都有详细注释,此处不再阐述):

struct _field_control_s {                                           

sal_mutex_t           fc_lock;       /* Protectionmutex.             */

   bcm_field_stage_t     
 stage;        /* Default FP pipeline stage.   */

   int                   max_stage_id;  /* Number of fpstages.          */

   _field_udf_t           udf;          / field_status->group_total     */

   struct _field_group_s groups;       / List of groupsin unit.       */

struct_field_stage_s  stages;       / Pipeline stage FP info.  

}

2.      然后对field_control_t中的_field_group_s表示一种应用占用的slice和slice的规则记录:

_field_group_s {

   bcm_field_group_t      gid;            /* Opaque handle.                */

   int                   priority;       /* Field grouppriority.         */

   bcm_field_qset_t       qset;           /* This group’s Qualifier Set.   */

   uint8                  flags;          /* Group configuration flags.    */

   _field_slice_t        slices;        / Pointer intoslice array.     */

   bcm_pbmp_t             pbmp;           /* Ports in use this group.      */

   _field_sel_t sel_codes[_FP_MAX_ENTRY_WIDTH]; /* Select codes forslice(s). */

   _bcm_field_group_qual_t qual_arr[_FP_MAX_ENTRY_WIDTH];

                          
                /* Qualifiers available in each

                                             individual entry part.        */

_field_stage_id_t      stage_id;       /* FP pipeline stage id.         */

}

3.      在每一个unit中还有_field_stage_s来对各种FP(VFP/IFP/EFP)的资源记录的数据结构:

typedef struct _field_stage_s {

   _field_stage_id_t     stage_id;        /* Pipeline stageid.           */

   uint8                  flags;           /* Stage flags.                 */

   int                   tcam_sz;         /* Number ofentries in TCAM.   */

   int                   tcam_slices;     /* Number ofinternal slices.   */

struct_field_slice_s  slices;         / Array of slices.*/

}

4.      在在每一个_field_stage_s中用_field_slice_s对每一个slice资源进行记录的结构体:

_field_slice_s {

   uint8                 slice_number;  /* Hardware slicenumber.         */

   int                   start_tcam_idx;/* Slice first entry tcam index.  */

   int                   entry_count;   /* Number of entriesin the slice.*/

   int                   free_count;    /* Number of freeentries.        */

   int                   counters_count;/* Number of counters accessible. */

   int                   meters_count;  /* Number of metersaccessible.   */

   _field_counter_bmp_t  counter_bmp;   /* Bitmap forcounter allocation. */

   _field_meter_bmp_t    meter_bmp;     /* Bitmap for meterallocation.   */

   _field_stage_id_t     stage_id;      /* Pipeline stageslice belongs.  */

   bcm_pbmp_t   
         pbmp;          /* Ports in use by groups.        */

    struct _field_entry_s *entries;     / List of entriespointers.      */

}

5.      在在每一个_field_slice_s中用_field_entry_s对slice内部的entry进行记录:

struct_field_entry_s {

    bcm_field_entry_t      eid;       /* BCM unit unique entryidentifier   */

    int                    prio;       /* Entry priority                     */

    uint32                 slice_idx;  /* Field entry tcam index.            */

    uint16                 flags;      /* _FP_ENTRY_xxx flags                */

    _field_tcam_t          tcam;       /* Fields to be written intoFP_TCAM  */

    _field_tcam_t          extra_tcam;

#ifdefined(BCM_RAPTOR_SUPPORT) || defined(BCM_TRX_SUPPORT)

    _field_pbmp_t          pbmp;      /* Port bitmap */

#endif /*BCM_RAPTOR_SUPPORT || BCM_TRX_SUPPORT */   

    _field_action_t        actions;   / linked list of actions for entry   */

    _field_slice_t         fs;        / Slice where entry lives            */

    _field_group_t         group;     / Group where entry lives            */

    _field_entry_stat_t    statistic; /* Statistics collection entity.     */          

                                       /*Policers attached to the entry.    */

    _field_entry_policer_tpolicer[_FP_POLICER_LEVEL_COUNT];

#ifdefined(BCM_KATANA_SUPPORT)

    _field_entry_policer_tglobal_meter_policer;

#endif

    struct _field_entry_s  next;     / Entry lookup linked list.         */

};

上面actions 是一个_field_action_t的结构体的链表,其信息为:

typedef struct_field_action_s {

    bcm_field_action_t     action;       /* action type               */

    uint32                 param0;       /* Action specific parameter */

    uint32                 param1;       /* Action specific parameter */

    uint8                  inst_flg;     /* Installed Flag            */

    struct _field_action_s *next;

}_field_action_t;

6.      在SDK编码中,用UNIT号获取对应的_field_control_t信息的代码可以如下:

_field_control_t    *fc;

BCM_IF_ERROR_RETURN(_field_control_get(unit,&fc));

7.      进而获取每一个group资源的代码可以如下:

 _field_group_t      *fg;

 fg = fc->groups;

    while (fg != NULL) {

        if (fg->gid == gid) {

            *group_p = fg;

            return (BCM_E_NONE);

        }

        fg = fg->next;

    }

8.      获取每一个slice的资源可以如下

_field_slice_t         *slices;

slice =&fg->slices[0];

while(slice !=NULL){

slice = slice->prev;

}

9.      获取slice中规则的的资源可以如下:

_field_entry_t  *f_ent;

 _field_action_t  *fa_iter;

_field_entry_get(unit, entry, _FP_ENTRY_PRIMARY,&f_ent);//entry

fa_iter = f_ent->actions;//entry的action

熟悉FP同学可能深知FP资源的稀缺性和重要性,可以用惜slice如黄金来做比喻;虽然FP的规则数很多,但是FP的资源申请和释放是按照slice为单位来进行的,且slice的数目一般都不是很多;所以我们要将尽量多的规则整合到一个slice里,尽量减少slice里有规则被浪费的现象;这个也是再将来的协议改造中必须考虑到的一个因素。

到这里对FP的原理和SDK的相关数据结构介绍到这里,如果描述中有不清晰或者不准确的地方欢迎随时沟通讨论。


BCM芯片FP原理及相关SDK数据结构介绍
https://dnsnat.gitee.io/EMBEDDED/BCM芯片FP原理及相关SDK数据结构介绍.html
作者
dnsnat
发布于
2022年3月22日
许可协议