SAI
====
###### tags: `SONiC`
[Switch Abstraction Interface v0.9.1](https://github.com/opencomputeproject/SAI/blob/master/doc/SAI-v0.9.1.pdf)
[SAI Design Spec](https://github.com/opencomputeproject/SAI/blob/master/doc/spec.md)
[SAI version for sonic release](https://github.com/Azure/SONiC/blob/master/doc/SONiC_202012_Release_Notes.md)
[Broadcom SDK](https://github.com/Broadcom-Network-Switching-Software/OpenBCM)
# SAI Main Concept
### 1. Adapter
pluggable code module, supplied by either a vendor or control plane stack owner, that contains either ASIC SDK code itself or client module for ASIC SDK hosted in external process and implements the interfaces described in this specification; for all practical purposes it is equivalent of a “user-mode driver”.
Switching adapters are user-mode drivers, typically supplied by ASIC vendors. Adapters are registered with the switching stack and then can be loaded as needed. It is a responsibility of the adapter to discover and bind to the specified underlying hardware, including loading of or attaching to kernel-mode drivers if needed.
Adapters are expected to be as simple as possible, ideally simple wrappers around vendor’s SDKs. Our design strives to push the bookkeeping complexity from adapter into the adapter host wherever possible.
### 2. Adapter host (Syncd Container)
Adapter Host is provided with the information about binary module with adapter code that must be loaded during startup.
After adapter module load, adapter host acquires addresses of the three well-known functions: `sai_api_initialize()`, `sai_api_query()` and `sai_api_uninitialize ()`.
```clike
sai_api_initialize()
sai_api_query()
sai_api_uninitialize()
```
#### `sai_api_initialize` caller point
Function is supplied with method table of services provided by the adapter host to adapter module. Note: SDK initialization should NOT be performed here. This function is just to take care of any platform-specific differences related to module loading, and generally should be very simple.
src/sonic-sairedis/syncd/Syncd.cpp
```clike=
sai_status_t status = vendorSai->initialize(0, &m_test_services);
```
src/sonic-sairedis/syncd/VendorSai.cpp
```
auto status = sai_api_initialize(flags, service_method_table);
```
- [SAI-Implementation Mellanox](https://github.com/Mellanox/SAI-Implementation)
mlnx_sai/src/mlnx_sai_interfacequery.c
#### `sai_api_query()` Hook the Implement
After initialization, `sai_api_query()` can be used for retrieval of various methods tables for SAI functionalities.
- `platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_interfacequery.c`
- `platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_vlan.c`
```clike=
const sai_vlan_api_t mlnx_vlan_api = {
mlnx_create_vlan,
mlnx_remove_vlan,
mlnx_set_vlan_attribute,
mlnx_get_vlan_attribute,
mlnx_create_vlan_member,
mlnx_remove_vlan_member,
mlnx_set_vlan_member_attribute,
mlnx_get_vlan_member_attribute,
mlnx_create_vlan_members,
mlnx_remove_vlan_members,
mlnx_get_vlan_stats,
mlnx_get_vlan_stats_ext,
mlnx_clear_vlan_stats
};
```
### 3. Control Plane stack
### 4. Switching Entity
# Syncd (sairedis/syncd)
## Init vender SAI
src/sonic-sairedis/syncd/syncd.cpp
```c
auto vendorSai = std::make_shared<VendorSai>();
```
src/sonic-sairedis/syncd/Syncd.cpp
```clike=
sai_status_t status = vendorSai->initialize(0, &m_test_services);
```
- src/sonic-sairedis/syncd/Syncd.cpp
Syncd Main process, it will be used to `platform/syncd` container.
```c
void Syncd::run()
```
Monitor the event from redis DB (e.g. swss container will push the event into redis)
```c
result = s->select(&sel);
```
Process the event
```c
processEvent(*m_selectableChannel.get());
```
- `platform/<vender>/*`
Search the `sai_api_initialize`, SAI implement depend by platform.
e.g.`platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_interfacequery.c`
### Initialize the vender_sai
In Syncd constructer
`m_test_services = m_smt.getServiceMethodTable();`
`sai_status_t status = vendorSai->initialize(0, &m_test_services);`
### Uninitialize
`status = m_vendorSai->uninitialize();`
# Performs full SDK initialization
## SWSS Container (orchagent/sonic-swss)
```cpp=
- src/sonic-swss/orchagent/main.cpp
- src/sonic-swss/orchagent/saihelper.cpp
void initSaiApi()
sai_api_query(SAI_API_SWITCH,(void **)&sai_switch_api);
- src/sonic-swss/orchagent/saihelper.cpp
sai_switch_api_t* sai_switch_api;
- src/sonic-swss/orchagent/main.cpp
status = sai_switch_api->create_switch(&gSwitchId, (uint32_t)attrs.size(), attrs.data());
```
### sai_api_initialize
src/sonic-swss/orchagent/saihelper.cpp
```c=
void initSaiApi();
sai_api_initialize(0, (const sai_service_method_table_t *)&test_services);
```
src/sonic-sairedis/lib/src/sai_redis_interfacequery.cpp
- Global variable `redis_sai` is the key for SAI implmentaion.
`std::shared_ptr<Sai> redis_sai = std::make_shared<Sai>();`
```c=
redis_sai->initialize(flags, service_method_table);
```
src/sonic-sairedis/lib/src/Sai.cpp
```c=
sai_status_t Sai::initialize(
_In_ uint64_t flags,
_In_ const sai_service_method_table_t *service_method_table)
```
### sai_api_query
src/sonic-swss/orchagent/saihelper.cpp
```c=
sai_api_query(SAI_API_SWITCH, (void **)&sai_switch_api);
```
src/sonic-sairedis/lib/src/sai_redis_interfacequery.cpp
**Hook the API table into `sai_switch_api`**
Whole API table list:
```c=
#define API(api) .api ## _api = const_cast<sai_ ## api ## _api_t*>(&redis_ ## api ## _api)
static sai_apis_t redis_apis = {
API(switch), //redis_switch_api
API(port),
API(fdb),
API(vlan),
API(virtual_router),
...
```
Search by input table ID : `SAI_API_SWITCH = 1`
`*api_method_table = ((void**)&redis_apis)[sai_api_id - 1];`
src/sonic-sairedis/lib/src/sai_redis_switch.cpp
```c=
const sai_switch_api_t redis_switch_api = {
redis_create_switch_uniq,
redis_remove_switch,
redis_set_switch_attribute,
redis_get_switch_attribute,
REDIS_GENERIC_STATS_API(switch)
redis_switch_mdio_read,
redis_switch_mdio_write,
};
```
`redis_create_switch_uniq` => `redis_create_switch`
:::danger
Where is the redis_create_switch implement?
:::
The macro define of `redis_create_switch`.
```c=
REDIS_GENERIC_QUAD(SWITCH,switch);
#define REDIS_GENERIC_QUAD(OT,ot) \
REDIS_CREATE(OT,ot); \
REDIS_REMOVE(OT,ot); \
REDIS_SET(OT,ot); \
REDIS_GET(OT,ot);
#define REDIS_CREATE(OT,ot) \
static sai_status_t redis_create_ ## ot( \
_Out_ sai_object_id_t *object_id, \
_In_ sai_object_id_t switch_id, \
_In_ uint32_t attr_count, \
_In_ const sai_attribute_t *attr_list) \
{ \
SWSS_LOG_ENTER(); \
return redis_sai->create( \
(sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \
object_id, \
switch_id, \
attr_count, \
attr_list); \
}
```
Who is `redis_sai->create`?
The redis_sai is the global pointer in `src/sonic-sairedis/lib/src/sai_redis_interfacequery.cpp`.
```c=
`std::shared_ptr<Sai> redis_sai = std::make_shared<Sai>();
```
Where is `redis_sai->create` implementaion?
src/sonic-sairedis/lib/src/Sai.cpp //The implementation of sairedis.
```c=
sai_status_t Sai::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)
```
> Sai.cpp is the core implmetation of redis_sai or sairedis module.
> `sai_redis_switch.cpp` provide the `sai_api_query` input material. It will help to bind the implementation of `Sai::redis_sai` with `sai_switch_api` table.
# [SAI Config Model](https://github.com/Azure/SONiC/blob/gh-pages/doc/Sonic_workshop_config_model.pdf)
# Todo
```clike=
saitam.h
saiacl.h
saidtel.h
saiudf.h
saimirror.h
```
IPv6 & IPv4 Static Route
Policy-base routing**
ACL(IP/MAC/VLAN)
VTY Access-list
Port Security
QinQ
### Multi ASIC Held
[SONiC_multi_asic](https://github.com/Azure/SONiC/blob/ebe4f4b695af5d2dbd23756d3cff03aef0a0c880/doc/multi_asic/SONiC_multi_asic_hld.md)
[SAI-Implementation Mellanox](https://github.com/Mellanox/SAI-Implementation)
[SAI_implementation fish Guide](https://github.com/timetofish/practice/blob/main/sonic/TOI/README.SAI_implementation.md)
- EVPN
- VXLAN
- GRE
```cpp
typedef enum _sai_tunnel_type_t
{
SAI_TUNNEL_TYPE_IPINIP,
SAI_TUNNEL_TYPE_IPINIP_GRE,
SAI_TUNNEL_TYPE_VXLAN,
SAI_TUNNEL_TYPE_MPLS,
} sai_tunnel_type_t;
```
- Access Control Lists (ACL)
- Equal Cost Multi Path (ECMP)
- Forwarding Data Base (FDB, MAC address table)
- Host Interface
- Neighbor database, Next hop and next hop groups
- Port management
- Quality of Service (QoS)
- Route, router, and router interfaces
# OID, VID, RID, switch ID
SAI provide the Object ID for upper layer.
When `redis_create_switch` call, SAI will create the switch ID.
In SONiC, the sairedis OID is Virtual ID (VID).
In Syncd, the SAI OID is Real ID (RID).
Object ID mean the pointer to SAI_OBJECT.
```bash=
keys *ROUTE_ENTRY*
127.0.0.1:6379[1]> hgetall "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY:{\"dest\":\"10.33.33.0/24\",\"switch_id\":\"oid:0x21000000000000\",\"vr\":\"oid:0x3000000000022\"}"
1) "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID"
2) "oid:0x60000000005e7"
127.0.0.1:6379[1]> keys *0x60000000005e7*
1) "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE:oid:0x60000000005e7"
```
# SAI profile
- SAI profile locate at the system path `/usr/share/sonic/hwsku/sai.profile`.
- Original file locate at `src/sonic-device-data/src/sai.vs_profile`.
`sai.profile` is used for `service_method_table->profile_get_value`.
```clike=
service_method_table->profile_get_value(0, SAI_KEY_VS_SWITCH_TYPE);
```
- Device profile:
- Force10-S6000 t1: `device/virtual/x86_64-kvm_x86_64-r0/default_sku`
- `device/virtual/x86_64-kvm_x86_64-r0/README.md`
#### What is relation between Sai implmentation and Meta implementation.
```clike=
m_vsSai = std::make_shared<VirtualSwitchSaiInterface>(scc);
m_meta = std::make_shared<saimeta::Meta>(m_vsSai);
m_vsSai->setMeta(m_meta);
```