sonic syncd

1. syncd应用启动

1.1 syncd应用启动参数

syncd应用启动初始化时可以设置参数,如下图所示:
sonic syncd


-d
: enable dialoh shell


-p file
: “profile”
,指定
sai.profile
,形如:
sonic syncd


-t startType
: 启动方式,存在以下几种: – 区别????


STRING_SAI_START_TYPE_COLD_BOOT

STRING_SAI_START_TYPE_WARM_BOOT

STRING_SAI_START_TYPE_FAST_BOOT

STRING_SAI_START_TYPE_FASTFAST_BOOT

STRING_SAI_START_TYPE_UNKNOWN


-u
: enable useTempView – ?


-S
: disableExistSleep – ?


-U
: enableUnittests


-C
: enableConsistencyCheck – ?


-s
: “syncMode”,表示允许启动同步模式


-z mode
: “redisCommunicationMode”
,表示启用的通信模式,包括
SAI_REDIS_COMMUNICATION_MODE_REDIS_ASYNC

SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC

SAI_REDIS_COMMUNICATION_MODE_ZMQ_SYNC


-l
: enableSaiBulkSupport – ?


-g index
: “globalContext”
,指定全局上下文的序号,一般为0,配合
-x
使用


-x file
: “contextConfig”
,指定上下文配置文件,形如:
sonic syncd


-b breakConfig
: 只有在broadcom中配置了,文件内容为:
SAI_OBJECT_TYPE_ACL_TABLE
– ?


-r
: “rpcserver”,启用”rpcserver”,只有允许使用thrift机制才会启用,默认不启用


-m file
: “port_config.ini”,形如:
sonic syncd


-h
: “help”

最终barefoot的启动命令为:

/usr/bin/syncd -u -l -p /usr/share/sonic/hwsku/sai.profile

不支持热重启没有指定上下文配置不允许同步模式

1.2 syncd应用启动初始化

初始化启动位于
syncd_main.cpp
:
sonic syncd

设置syncd应用的日志存放检查syncd应用是否支持热重启 – 不支持解析syncd应用启动时设置的参数

默认信息
sonic syncd

设置的参数信息


m_enableTempView = true

m_enableSaiBulkSupport = true

m_profileMapFile = "/usr/share/sonic/hwsku/sai.profile"
是否配置了port map文件,如果有则解析并保存到全局变量
gPortMap
没有配置是否启用了rpc server,如果启用了,则创建thrift rpc server – 没有启用创建
VendorSai
对象 – 负责实际调用sdk – 重点初始化
Syncd
对象,并启用循环监听
, – 是syncd应用的重点对象,处理发布/订阅事件,建立与orchagent应用的通信通道

1.3
Syncd
对象构造

初始化启动位于
syncd.cpp
,看一下它的构造过程:
传入参数:
VendorSai
对象,启动参数
CommandLineOptions
,是否允许热重启
isWarmStart

成员变量赋初值


m_commandLineOptions

CommandLineOptions
: 参数传入
m_isWarmStart

bool
: 参数传入,
false
,是通过启动参数配置的
m_firstInitWasPerformed
:
false

m_asicInitViewMode
:
false

m_vendorSai

VendorSai
: 参数传入
m_veryFirstRun
:
false

m_enableSyncMode
:
false

m_timerWatchdog

TimerWatchdog
: 30 * 1000000 – 30s

根据api类型分别设置Log级别 –
SAI_LOG_LEVEL_NOTICE

根据启动参数获取上下文信息,如果参数没有设置,则获取默认的:

{"guid":0,"name":"syncd","dbAsic":"ASIC_DB","dbCounters":"COUNTERS_DB","dbFlex":"FLEX_COUNTER_DB","dbState":"STATE_DB","zmqEnable":"false","zmqEndpoint":"ipc:///tmp/zmq_ep","zmqNtfEndpoint":"ipc:///tmp/zmq_ntf_ep"}

如果上下文中允许使用ZeroMQ机制(
m_contextConfig->m_zmqEnable=true
),并且启动参数中允许同步模式(
m_commandLineOptions->m_enableSyncMode=true
),更新启动参数信息:


m_commandLineOptions->m_enableSyncMode = false;

m_commandLineOptions->m_redisCommunicationMode = SAI_REDIS_COMMUNICATION_MODE_ZMQ_SYNC;

不执行,因为
m_contextConfig->m_zmqEnable=false
&
m_commandLineOptions->m_enableSyncMode=false

如果启动参数中允许同步模式(
m_commandLineOptions->m_enableSyncMode=true
),更新配置信息:


m_enableSyncMode = true;

m_contextConfig->m_zmqEnable = false;

m_commandLineOptions->m_redisCommunicationMode = SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC;

不执行,因为
m_commandLineOptions->m_enableSyncMode=false

如果启动参数中设置的通信模式是基于ZeroMQ机制的同步模式(
m_commandLineOptions->m_redisCommunicationMode = SAI_REDIS_COMMUNICATION_MODE_ZMQ_SYNC
),更新配置信息:


m_contextConfig->m_zmqEnable = true;

m_enableSyncMode = true;

不执行,因为
m_commandLineOptions->m_redisCommunicationMode=SAI_REDIS_COMMUNICATION_MODE_REDIS_ASYNC
,默认配置,配置的是基于redis机制的异步模式。
上述三个判断说明如果支持ZeroMQ机制,那么将优先使用基于ZeroMQ机制的同步模式进行通信。

初始化成员变量
std::shared_ptr<FlexCounterManager> m_manager
: 管理
COUNTERS_DB
中的相关信息,如每个端口关联的计数器/统计信息

如果启动参数中设置了·
-p file
,那么加载配置文件(
sai.profile
)存入成员变量
m_profileMap
– 保存应用启动的配置信息:


SAI_VS_GLOBAL_CONTEXT
: 0
SAI_KEY_VS_CONTEXT_CONFIG
: NULL
SAI_KEY_WARM_BOOT_WRITE_FILE
:
var/warmboot/sai-warmboot.bin

SAI_KEY_WARM_BOOT_READ_FILE
:
/var/warmboot/sai-warmboot.bin

sai.profile
获取信息: 当前设置下就是
SAI_KEY_WARM_BOOT_WRITE_FILE
以及
SAI_KEY_WARM_BOOT_READ_FILE
的默认设置

初始化与
asic_db
的连接对象

判断是否允许启动ZeroMQ机制,初始化成员变量
std::shared_ptr<NotificationProducerBase> m_notifications
以及
std::shared_ptr<sairedis::SelectableChannel> m_selectableChannel
:

允许(
m_contextConfig->m_zmqEnable=true
),构建ZeroMQ通信通道:
使用
ZeroMQNotificationProducer
子类初始化
m_notifications
使用
ZeroMQSelectableChannel
子类初始化
m_selectableChannel
,对应orchagent应用中的
ZeroMQChannel
通信
m_enableSyncMode = true;
不允许(
m_contextConfig->m_zmqEnable=false
,构建redis通信通道:
使用
RedisNotificationProducer
子类初始化
m_notifications
使用
RedisSelectableChannel
子类初始化
m_selectableChannel
,对应orchagent应用中的
RedisChannel
通信
m_enableSyncMode = m_commandLineOptions->m_redisCommunicationMode == SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC;
– false
m_notifications
: 一个通知发布对象
m_selectableChannel
: 监听来自orchagent应用的事件并且处理,与orchagent应用中的
m_communicationChannel
对应

初始化notification相关成员对象,启动notification处理线程,写入asic_db: switch相关事件通知,比如交换机状态变化、端口状态变化、fdb事件等,有两个来源:

syncd应用启动的时候: 根据配置文件得到热重启能力,然后更新启动类型接收来自orchagent应用的通知: 如果通知中的object类型是
SAI_OBJECT_TYPE_SWITCH
,并且是create或者update操作,需要发布通知,写入asic_db接收来自
ASIC_DB RESTARTQUERY CHANNEL
的通知并处理

初始化
FLEX_COUNTER_DB
相关表的订阅与监听:


FLEX_COUNTER_TABLE
: 保存的数据指什么??
FLEX_COUNTER_GROUP_TABLE
: 保存的数据指什么?

初始化成员对象
std::shared_ptr<sairedis::VirtualObjectIdManager> m_virtualObjectIdManager
,是一个通过asic_db中一个计数器进行虚拟oid管理的对象,属于sonic层面的object id,而不是实际在asic的oid

初始化成员对象
std::shared_ptr<VirtualOidTranslator> m_translator
– 管理vid与rid的对应关系:

vid: 表示虚拟的object id,用于在orchagent以及syncd之间作为参数传递rid: 表示调用sdk真正创建了对象之后得到的object id判断是否是第一次运行,赋值标志位
m_veryFirstRun
:
HGETALL HIDDEN
,存在信息则不是第一次运行
sonic syncd

根据当前启动参数判断启动类型,并且更新
m_profileMap
对象中的
SAI_BOOT_TYPE
信息 –
performStartupLogic()
,根据当前设置是冷启动,所以:

m_profileMap[SAI_KEY_BOOT_TYPE] = "SAI_START_TYPE_COLD_BOOT";

SAI_START_TYPE_COLD_BOOT = 0

调用
vendorSai->initialize()
:


sai_api_initialize()
: – ??
sai_metadata_apis_query()
: –?

初始化成员
std::shared_ptr<BreakConfig> m_breakConfig
: – ??

启动循环
Syncd::run()

初始化热重启table对象:
STATE_DB:WARM_RESTART_TABLE
设置关机类型参数: 初始化设置为冷启动类型设置循环标志位:
runMainLoop = true
初始化
Select
的一个共享指针对象
s
: 用于管理当前应用进程的事件通知对象
onSyncdStart()
: 对switch info的热重启与硬重启处理,目前启动参数配置为不允许热重启,所以会进行一次新的冷启动,创建switch相关信息,更新数据库中的switch、fdb、neighbors等信息 – ???启动notification线程,处理各种通知,写入asic_db – 重点将构造函数中初始化的消费者对象加入
s
开启循环,
s->select()
首先通过
epoll
获取内核中的事件通知,然后每一个消费对象读取对应的事件通知,按照时间戳顺序轮询选择消费者对象进行处理,一共存在四个消费者对象:
处理来自
asic_db

RESTARTQUERY
的通知:
NotificationConsumer
处理来自
FLEX_COUNTER_DB

FLEX_COUNTER_TABLE
的通知:
ConsumerTable
处理来自
FLEX_COUNTER_DB

FLEX_COUNTER_GROUP_TABLE
的通知:
ConsumerTable
处理来自
orchagent
应用通过
asic_db

syncd
应用的通知
– 重点 如果循环标志位被置为
false
,那么syncd应用也结束了:
如果是热重启,将结束信息以及一些标志位写入
state_db

WARM_RESTART_TABLE
中清空计数器移除switch实例 – ???结束通知线程取消sai初始化 –
VendorSai::uninitialize()

2 事件通知预处理

2.1 通知消息读取

sonic syncd

上述过程是读取内核中的事件通知过程:

epoll_wait查询当前存在的连接是否有events遍历得到的fd,针对每个fd:
调用
Selectable::readData()
读取内核中events – 具体怎么处理,看用的是哪一个子类将fd存入一个有序集合
m_ready
– 该集合表示当前已经获取到events的连接,按照连接上次使用的时间戳进行排序,时间戳小排在前面 – 即处理等待时间长的events 遍历该有序集合
m_ready
:获取集合最前面的订阅对象
从集合中删除,并且更新时间戳如果该订阅对象没有events,继续循环如果该订阅对象有events,返回该对象作为待处理的events对象;如果不只一条event,重新将该fd插入到有序集合中更新订阅对象中待处理的消息数量,退出循环

在添加消费者的过程中实际上存在两类消费者,一类是
NotificationConsumer
,一类是
ConsumerTable
,对于事件监听与读取的过程不完全相同。
事件通知读取 – readData():
首先
s->select()
会判断当前是否有事件通知,如果有,针对有事件通知的每一个消费者对象从内核中读取事件通知:

如果是
ConsumerTable
,此处与orchagent应用不同,因为这里订阅使用的是redis的list机制,实际上通知详细信息是存储在list中,而发布的通知只是针对对应频道发布了一个空的事件通知。所以这里只是读取了事件通知的数量,以便判断当前消费者对象是否存在需要处理的事件通知:

PUBLISH ASIC_STATE_CHANNEL G
如果是
NotificationConsumer
,将直接读取通知消息存储到成员
m_queue
队列中

2.2 事件通知预处理 – pop()

以单个事件处理为例。首先分析一下从队列中取出一个事件的过程:
事件通知预处理 – pop():

如果是
ConsumerTable
,以acl为例,成员
m_buffer
是待处理的事件通知队列,如果为空,则首先访问redis数据库获取详细的事件通知 –
ConsumerTable::pops()
:
使用
EVALSHA
命令调用
consumer_table_pops.lua
脚本: 当前配置是读取redis的
ASIC_STATE_KEY_VALUE_OP_QUEUE
队列的事件通知(与RedisRemoteSaiInterface章节中对应),然后将信息写入
ASIC_DB

ASIC_STATE
表中,记录的
key
值形如
ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE:oid:0x%
然后解析事件通知,每一个事件通知格式为
<key,op,values>
,存储为一个
vector
存储的是序列化的值如果选择了这个消费者对象,从
m_buffer
中取出事件通知进行处理 如果是
NotificationConsumer
: 如果选择了这个消费者对象,从
m_queue
中取出事件通知进行处理

sonic syncd

上图是事件通知的通用处理操作:

从key值反序列出对应的object type以及object id:


key
:
SAI_OBJECT_TYPE_ACL_TABLE:oid:0x%
反序列化其实就是拆解字符串:
{"object type":"SAI_OBJECT_TYPE_ACL_TABLE", "oid":"oid:0x%"}

判断object type是否有效

构造属性列表对象,主要是检查属性的有效性,以及针对属性value的反序列化
sonic syncd

如果事件类型是
SAI_OBJECT_TYPE_SWITCH
,需要更新每个通知信息对应的callback函数

isInitViewMode(): 当前配置信息是false,所以不执行

如果不是get操作,需要将所有属性的vid转换为rid

调用sdk库获取object type对应的meta信息
sonic syncd

根据meta信息判断调用哪一个处理方法,以acl为例,调用
Syncd::processOid()
处理事件通知,写入asic: 根据不同的操作继续向下调用不同的处理方法,每个操作对于不同object type的对象处理方法是同样的 – 在下一个小节”acl事件处理”中会进行详细介绍


sai_status_t processOid(
    _In_ sai_object_type_t objectType,
    _In_ const std::string &strObjectId,
    _In_ sai_common_api_t api,
    _In_ uint32_t attr_count,
    _In_ sai_attribute_t *attr_list);


objectType
: 对象类型,如
SAI_OBJECT_TYPE_ACL_TABLE

strObjectId
: 如
oid:0x%

api
: 操作类型,如
SAI_COMMON_API_CREATE

attr_count
: 对象的属性个数
attr_list
: 对象的属性信息列表

当前操作如果是GET操作,需要向asic_db写入响应;如果是其他操作,如果是同步模式则需要写入响应,如果是异步模式,则直接返回成功 – 响应的写入与读取也是基于redis的订阅/发布机制,不再赘述

3 acl事件处理

在处理各种操作之前,调用sdk库获取object type对应的meta信息 –
info

3.1 CREATE

调用
processOidCreate()
方法处理
create
操作:


sai_status_t processOidCreate(
    _In_ sai_object_type_t objectType,
    _In_ const std::string &strObjectId,
    _In_ uint32_t attr_count,
    _In_ sai_attribute_t *attr_list);

主要步骤只有两步:

调用
VendorSai::create()
调用sai接口的具体实现如果创建成功,会得到rid,将vid与rid的对应关系写入asic_db

sonic syncd

主要看一下每一个方法向下调用过程中的参数变化与处理:


VendorSai::create()


 virtual sai_status_t create(
    _In_ sai_object_type_t objectType,
    _Out_ sai_object_id_t* objectId,
    _In_ sai_object_id_t switchId,
    _In_ uint32_t attr_count,
    _In_ const sai_attribute_t *attr_list)


objectType
: 对象类型,如
SAI_OBJECT_TYPE_ACL_TABLE

objectId
: 传递指针,将创建成功以后的oid赋值给该参数

switchId

attr_count
: 对象的属性个数
attr_list
: 对象的属性信息列表
处理:
调用sdk库获取object type对应的meta信息
info
,包括类型的基本属性信息、sai对应的crud方法信息等确认
info
中存在
create
方法调用
info->create()
得到
object id
赋值给参数
objectId

info->create()

最终调用的是sdk中
saimetadata.c
中的
sai_metadata_generic_create_SAI_OBJECT_TYPE_ACL_TABLE()
方法:


sai_status_t sai_metadata_generic_create_SAI_OBJECT_TYPE_ACL_TABLE(
    _Inout_ sai_object_meta_key_t *meta_key,
    _In_ sai_object_id_t switch_id,
    _In_ uint32_t attr_count,
    _In_ const sai_attribute_t *attr_list)
{
    return sai_metadata_sai_acl_api->create_acl_table(&meta_key->objectkey.key.object_id, switch_id, attr_count, attr_list);
}


meta_key
:
{ .objecttype = objectType, .objectkey = { .key = { .object_id = 0 } } }
– 最终
.objectkey = { .key = { .object_id = 0 } }
保存创建完成以后的oid
switchId

attr_count
: 对象的属性个数
attr_list
: 对象的属性信息列表
然后与orchagent应用中调用sairedis api的过程类似,
sai_metadata_sai_acl_api
是一个全局变量,通过
sai_api_initialize()
&
sai_api_query()
方法赋值,最终赋值为
saiacl.app
中的全局变量
acl_api
。所以上述最终调用的方法是:


sai_status_t sai_create_acl_table(_Out_ sai_object_id_t *acl_table_id,
    _In_ sai_object_id_t switch_id,
    _In_ uint32_t attr_count,
    _In_ const sai_attribute_t *attr_list) 

其中
acl_table_id
: 是
meta_key

.objectkey
的引用,有以下几个步骤:
检查属性列表与table类型是否匹配将属性列表信息转化为符合switch store的对应属性信息最后调用
bf_switch_object_create()
方法
bf_switch_object_create()


switch_status_t bf_switch_object_create(const switch_object_type_t  object_type, 
    const std::set<smi::attr_w> &attrs,
    switch_object_id_t &object_handle) {
    return smi::api::smi_object_create(object_type, attrs, object_handle);
}


object_type
: 如
SAI_OBJECT_TYPE_ACL_TABLE

attrs
: 转换之后的属性列表
object_handle
: 将得到的oid赋值给该参数
smi::api::smi_object_create()


switch_status_t smi_object_create(const switch_object_type_t object_type,
    const std::set<attr_w> &attrs,
    switch_object_id_t &object_handle);

检查当前类型的table是否支持这些属性调用
switch_store::object_create()
创建table
switch_store::object_create()
– 写入switch store,也是写入asic的最终执行


switch_status_t object_create(const switch_object_type_t object_type,
    const std::set<attr_w> &attrs,
    switch_object_id_t &object_id);

检查object type以及属性信息 – 比上一步骤中的检查更底层一点??检查triggers,如果存在create之前执行的trigger,执行
oid_create()
: 先分配object id
db::object_create()
: 参数为object id,在switch store中创建这个hash值将属性信息写入switch store对应hash值中
create_keys()
: – ???检查triggers,如果存在create之后执行的trigger,执行
create_membership()
: – ??

未完待续…

© 版权声明

相关文章

暂无评论

none
暂无评论...