SAI

SAI(Switch Abstraction Interface,交换机抽象接口)是SONiC的基石,正因为有了它,SONiC才能支持多种硬件平台。我们在这个SAI API的文档中,可以看到它定义的所有接口。

在核心容器一节中我们提到,SAI运行在syncd容器中。不过和其他组件不同,它并不是一个服务,而是一组公共的头文件和动态链接库(.so)。其中,所有的抽象接口都以c语言头文件的方式定义在了OCP的SAI仓库中,而.so文件则由各个硬件厂商提供,用于实现SAI的接口。

SAI接口

To have a more intuitive understanding, let's take a small part of the code to show how the SAI interface is defined and initialized, as follows:

// File: meta/saimetadata.h
typedef struct _sai_apis_t {
    sai_switch_api_t* switch_api;
    sai_port_api_t* port_api;
    ...
} sai_apis_t;

// File: inc/saiswitch.h
typedef struct _sai_switch_api_t
{
    sai_create_switch_fn                   create_switch;
    sai_remove_switch_fn                   remove_switch;
    sai_set_switch_attribute_fn            set_switch_attribute;
    sai_get_switch_attribute_fn            get_switch_attribute;
    ...
} sai_switch_api_t;

// File: inc/saiport.h
typedef struct _sai_port_api_t
{
    sai_create_port_fn                     create_port;
    sai_remove_port_fn                     remove_port;
    sai_set_port_attribute_fn              set_port_attribute;
    sai_get_port_attribute_fn              get_port_attribute;
    ...
} sai_port_api_t;

where the sai_apis_t structure is a collection of interfaces for all SAI modules, where each member is a pointer to a list of interfaces for a particular module. Let's use sai_switch_api_t as an example, which defines all the interfaces of the SAI Switch module, which we can see defined in inc/saiswitch.h. Similarly, we can see the interface definitions for the SAI Port module in inc/saiport.h.

SAI初始化

The initialization of the SAI is actually figuring out how to get these function pointers above so that we can operate the ASIC through the SAI's interface.

There are two main functions involved in the initialization of the SAI, both of which are defined in inc/sai.h:

  • sai_api_initialize:初始化SAI
  • sai_api_query:传入SAI的API的类型,获取对应的接口列表

While most vendors' SAI implementations are closed source, mellanox has open sourced its own SAI implementation, so here we can use it to understand more deeply how SAI works.

For example, the sai_api_initialize function actually simply sets sets two global variables and returns SAI_STATUS_SUCCESS:

// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_interfacequery.c
sai_status_t sai_api_initialize(_In_ uint64_t flags, _In_ const sai_service_method_table_t* services)
{
    if (g_initialized) {
        return SAI_STATUS_FAILURE;
    }
    // Validate parameters here (code omitted)

    memcpy(&g_mlnx_services, services, sizeof(g_mlnx_services));
    g_initialized = true;
    return SAI_STATUS_SUCCESS;
}

After initialization, we can use the sai_api_query function to query the corresponding interface list by passing in the type of the API, and each interface list is actually a global variable: the

// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_interfacequery.c
sai_status_t sai_api_query(_In_ sai_api_t sai_api_id, _Out_ void** api_method_table)
{
    if (!g_initialized) {
        return SAI_STATUS_UNINITIALIZED;
    }
    ...

    return sai_api_query_eth(sai_api_id, api_method_table);
}

// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_interfacequery_eth.c
sai_status_t sai_api_query_eth(_In_ sai_api_t sai_api_id, _Out_ void** api_method_table)
{
    switch (sai_api_id) {
    case SAI_API_BRIDGE:
        *(const sai_bridge_api_t**)api_method_table = &mlnx_bridge_api;
        return SAI_STATUS_SUCCESS;
    case SAI_API_SWITCH:
        *(const sai_switch_api_t**)api_method_table = &mlnx_switch_api;
        return SAI_STATUS_SUCCESS;
    ...
    default:
        if (sai_api_id >= (sai_api_t)SAI_API_EXTENSIONS_RANGE_END) {
            return SAI_STATUS_INVALID_PARAMETER;
        } else {
            return SAI_STATUS_NOT_IMPLEMENTED;
        }
    }
}

// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_bridge.c
const sai_bridge_api_t mlnx_bridge_api = {
    mlnx_create_bridge,
    mlnx_remove_bridge,
    mlnx_set_bridge_attribute,
    mlnx_get_bridge_attribute,
    ...
};


// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_switch.c
const sai_switch_api_t mlnx_switch_api = {
    mlnx_create_switch,
    mlnx_remove_switch,
    mlnx_set_switch_attribute,
    mlnx_get_switch_attribute,
    ...
};

SAI的使用

In the syncd container, SONiC will start the syncd service at boot time, and the syncd service will load the SAI component currently on the system. This component is provided by individual vendors, who will implement the interface to the SAI shown above according to their own hardware platforms, thus allowing SONiC to control many different hardware platforms using a unified upper layer logic.

We can verify this simply by using the ps, ls and nm commands:

# Enter into syncd container
admin@sonic:~$ docker exec -it syncd bash

# List all processes. We will only see syncd process here.
root@sonic:/# ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
...
root          21  0.0  0.0  87708  1532 pts/0    Sl   16:20   0:00 /usr/bin/dsserve /usr/bin/syncd --diag -u -s -p /etc/sai.d/sai.profile -b /tmp/break_before_make_objects
root          33 11.1 15.0 2724396 602532 pts/0  Sl   16:20  36:30 /usr/bin/syncd --diag -u -s -p /etc/sai.d/sai.profile -b /tmp/break_before_make_objects
...

# Find all libsai*.so.* files.
root@sonic:/# find / -name libsai*.so.*
/usr/lib/x86_64-linux-gnu/libsaimeta.so.0
/usr/lib/x86_64-linux-gnu/libsaimeta.so.0.0.0
/usr/lib/x86_64-linux-gnu/libsaimetadata.so.0.0.0
/usr/lib/x86_64-linux-gnu/libsairedis.so.0.0.0
/usr/lib/x86_64-linux-gnu/libsairedis.so.0
/usr/lib/x86_64-linux-gnu/libsaimetadata.so.0
/usr/lib/libsai.so.1
/usr/lib/libsai.so.1.0

# Copy the file out of switch and check libsai.so on your own dev machine.
# We will see the most important SAI export functions here.
$ nm -C -D ./libsai.so.1.0 > ./sai-exports.txt
$ vim sai-exports.txt
...
0000000006581ae0 T sai_api_initialize
0000000006582700 T sai_api_query
0000000006581da0 T sai_api_uninitialize
...

参考资料

  1. SONiC Architecture
  2. SAI API
  3. Forwarding Metamorphosis: Fast Programmable Match-Action Processing in Hardware for SDN
  4. Github: sonic-net/sonic-sairedis
  5. Github: opencomputeproject/SAI
  6. Arista 7050QX Series 10/40G Data Center Switches Data Sheet
  7. Github repo: Nvidia (Mellanox) SAI implementation