Intel DPDK源代碼分析

Intel DPDK

源代碼分析

2013年8月

作者:    王智民

貢獻者:    

創建時間:    2013-8-8

穩定程度:    初稿

修改歷史

 

版本

日期

修訂人

說明

1.0

2013-8-8

王智民

初稿

       
       

目錄

1    引言    1

1.1    編寫目的    1

1.2    背景    1

2    框架分析    1

2.1    0層分解    1

2.2    1層分解    3

2.3    2層分解    4

2.3.1    Lib的核心組件    4

2.3.2    DPDK依賴於linux內核有哪些    4

2.3.3    DPDK如何做到用戶空間直接訪問網絡IO    5

2.3.4    DPDK如何管理接口    6

2.3.5    DPDK如何與linux交互報文    7

2.3.6    DPDK如何支持Intel網卡的SR-IOV或VMDq模式    9

2.3.7    採用DPDK EAL的應用程序如何運行    10

2.3.8    DPDK如何管理報文及對jumbo frame的支持    12

2.3.9    DPDK的應用程序之間如何通信    14

2.3.10    DPDK提供的幾種應用程序運行模式    15

2.3.11    DPDK如何支持multi-chips的    15

2.3.12    如何用DPDK開發數據通信產品    17

3    案例分析—6wind    21

3.1    網絡設備框架    21

3.2    VIRTUAL FAST PATH    24

3.3    VNB    24

3.4    管理框架    24

4    總結    25

5    附錄    26

5.1    本子系統用到的縮寫詞、定義和術語    26

5.2    參考資料    26

 

 

  1. 引言

    1. 編寫目的

本文檔結合手冊和軟件包分析了Intel DPDK的框架和一些關鍵問題的解決方案。

  1. 背景

Intel DPDK爲一些採用Intel芯片的網絡廠商和安全廠商提供了一個完整的開發環境,在業界引起了不小的轟動,同時多家安全廠商由於自身實力的限制,很歡迎這種免費或者代價不昂貴的解決方案,而Intel也很樂意做這樣的事情。

有代表性的廠商比如WindRiver,前期以做vxWorks操作系統非常知名,後來被Intel收購後全面切向支持Intel的系列軟件解決方案。

一方面Intel推出的Sandy Bridge芯片,CPU的IO方面做了一定的改進,號稱大包可以做到5元組轉發40G的處理能力,另外一方面,Intel藉助WindRiver的軟件解決能力,提出DPDK的數據處理解決方案,所以值得分析DPDK。

  1. 框架分析

    1. 0層分解

0層分解主要描述DPDK與其他外部的關係。

 

DPDK是Intel在linux用戶空間提供的一套數據轉發與處理的運行框架,其目的是提高運行在Intel的系列(從Atom到Xeon系列)的linux系統的數據報文吞吐能力。

DPDK類似一個小的操作系統,所以凡是操作系統涉及的內容在DPDK裏面都應該需要提供相應的API接口和實現,主要有:

• Intel® DPDK loading and launching

• Support for multi-process and multi-thread execution types

• Core affinity/assignment procedures

• System memory allocation/de-allocation

• Atomic/lock operations

• Time reference

• PCI bus access

• Trace and debug functions

• CPU feature identification

• Interrupt handling

• Alarm operations

 

  1. 1層分解

 

 

  1. 2層分解

    1. Lib的核心組件

DPDK提供的庫基本上實現小型操作系統的相關功能。其核心組件如下:

  1. DPDK依賴於linux內核有哪些

DPDK目的是在linux用戶空間打造出一套報文轉發處理的框架,但是下面的一些資源不得不需要修改或依賴於linux內核:

  1. 用戶空間訪問網絡接口驅動uio模塊

需要linux內核加載uio.ko模塊,同時需要加載DPDK生成的igb_uio.ko模塊,使得DPDK可以在用戶空間直接訪問到網絡接口IO。

  1. 大頁面支持(DPDK支持huge page,所以需要連續的大內存支持)

Linux內核配置需要使能HUGETLBFS。同時PROC_PAGE_MONITOR特性也需要打開。

在linux啓動的時候需要爲DPDK預留部分hugepages,所以做如下的設置:

For 2 MB pages, just pass the hugepages option to the kernel. For example, to reserve 1024 pages of 2 MB, use:

hugepages=1024

For other hugepage sizes, for example 1G pages, the size must be specified explicitly and can also be optionally set as the default hugepage size for the system. For example, to reserve 4G of hugepage memory in the form of four 1G pages, the following options should be passed to the kernel:

default_hugepagesz=1G hugepagesz=1G hugepages=4

Note: The hugepage sizes that a CPU supports can be determined from the CPU flags.If "pse"exists, 2M hugepages are supported; if "pdpe1gb" exists, 1G hugepages are supported.

Note: For 64-bit applications, it is recommended to use 1 GB hugepages if the platform supports them.

Note: If using a large number of hugepages, for example, 500 or more, it is advisable toincrease the open files limit of the current login session to prevent errors when running Intel® DPDK applications. Increasing the limit can be done using the ulimit command, ulimit -Sn 2048 for example.

In the case of a dual-socket NUMA system, the number of hugepages reserved at boot time is generally divided equally between the two sockets (on the assumption that sufficient memory is present on both sockets).

  1. HEPT時鐘(不是必選,可以用RTC)

需要在BIOS設置。

  1. DPDK如何做到用戶空間直接訪問網絡IO

DPDK提供兩種方式:一種是用戶空間接管IO,另外一種方式是用戶空間通過FIFO共享內存方式訪問linux內核管理的IO。

先分析第一種方式。

通過igb_uio.ko內核模塊來實現用戶空間訪問IO,同時igb_uio.ko依賴於linux本身的模塊uio.ko。實現在igb_uio.c文件。

1)PCI網卡驅動接管

igbuio_pci_probe()à

    udev->info.handler = igbuio_pci_irqhandler;

    udev->info.irqcontrol = igbuio_pci_irqcontrol;

    udev->info.priv = udev;

    udev->pdev = dev;

…

    

    if (uio_register_device(&dev->dev, &udev->info))

2)如何從PCI網卡收發報文

eth_igb_dev_init()-à

…

    eth_dev->rx_pkt_burst = &eth_igb_recv_pkts;

    eth_dev->tx_pkt_burst = &eth_igb_xmit_pkts;

…

第二種方式:rte_kni.c

The Intel® DPDK Kernel NIC Interface (KNI) allows userspace applications access to the Linux* control plane.

The benefits of using the Intel® DPDK KNI are:

• Faster than existing Linux TUN/TAP interfaces (by eliminating system calls and

copy_to_user()/copy_from_user() operations.

• Allows management of Intel® DPDK ports using standard Linux net tools such as

ethtool, ifconfig and tcpdump.

• Allows an interface with the kernel network stack.

kni_net.c文件裏面主要提供FIFO通信接口,用戶空間如何通過FIFO發送和接收報文。

  1. DPDK如何管理接口

DPDK提供兩種方式:一種是用戶空間接管IO,另外一種方式是用戶空間通過FIFO共享內存方式訪問linux內核管理的IO。

如果是第一種方式,那麼相當於Linux內核無法感知到相關的IO接口(至少是DPDK接管了中斷,比如接口鏈路狀態的中斷等),如果DPDK不接管IO的所有中斷或處理,那麼則接口IO一部分由Linux來管理,一部分由DPDK來管理。如果DPDK完全接管IO,那麼則存在下面幾個問題如何解決?

  1. 開源的一些應用程序需要修改有關接口的處理,以適應DPDK的EAL

  2. DPDK如何與Linux內核通信

    1. DPDK如何與linux交互報文

DPDK提供了一種exception path的解決方案,即DPDK與Linux內核之間通過tun/tap邏輯接口來實現相互的報文發送與接收,業務設計者可以決定哪些報文需要從DPDK送到linux內核繼續處理,或反之從linux內核的報文送到DPDK處理。

代碼分析:

static __attribute__((noreturn)) int main_loop(__attribute__((unused)) void *arg)

{

    …

    

    rte_snprintf(tap_name, IFNAMSIZ, "tap_dpdk_%.2u", lcore_id);

    tap_fd = tap_create(tap_name);

    if (tap_fd < 0)

        FATAL_ERROR("Could not create tap interface \"%s\" (%d)",

         tap_name, tap_fd);



    if ((1 << lcore_id) & input_cores_mask) {//可以設置哪些core從網口收上來的報文需要送到linux

        PRINT_INFO("Lcore %u is reading from port %u and writing to %s",

         lcore_id, (unsigned)port_ids[lcore_id], tap_name);

        fflush(stdout);

        

        for (;;) {

            struct rte_mbuf *pkts_burst[PKT_BURST_SZ];

            unsigned i;

            const unsigned nb_rx =

                    rte_eth_rx_burst(port_ids[lcore_id], 0,

                     pkts_burst, PKT_BURST_SZ);

            lcore_stats[lcore_id].rx += nb_rx;

            for (i = 0; likely(i < nb_rx); i++) {

                struct rte_mbuf *m = pkts_burst[i];

                

                int ret = write(tap_fd,

                 rte_pktmbuf_mtod(m, void*),

                 rte_pktmbuf_data_len(m));

                rte_pktmbuf_free(m);

                if (unlikely(ret < 0))

                    lcore_stats[lcore_id].dropped++;

                else

                    lcore_stats[lcore_id].tx++;

            }

        }

    }

    else if ((1 << lcore_id) & output_cores_mask) {//可以設置linux的報文送到哪些core上面去

        PRINT_INFO("Lcore %u is reading from %s and writing to port %u",

         lcore_id, tap_name, (unsigned)port_ids[lcore_id]);

        fflush(stdout);

        

        for (;;) {

            int ret;

            struct rte_mbuf *m = rte_pktmbuf_alloc(pktmbuf_pool);

            if (m == NULL)

                continue;



            ret = read(tap_fd, m->pkt.data, MAX_PACKET_SZ);

            lcore_stats[lcore_id].rx++;

            if (unlikely(ret < 0)) {

                FATAL_ERROR("Reading from %s interface failed",

                 tap_name);

            }

            m->pkt.nb_segs = 1;

            m->pkt.next = NULL;

            m->pkt.pkt_len = (uint16_t)ret;

            m->pkt.data_len = (uint16_t)ret;

            ret = rte_eth_tx_burst(port_ids[lcore_id], 0, &m, 1);

            if (unlikely(ret < 1)) {

                rte_pktmbuf_free(m);

                lcore_stats[lcore_id].dropped++;

            }

            else {

                lcore_stats[lcore_id].tx++;

            }

        }

    }

    else {

        PRINT_INFO("Lcore %u has nothing to do", lcore_id);

        for (;;)

            ;

    }

    

}

 

  1. DPDK如何支持Intel網卡的SR-IOV或VMDq模式

Supported Intel® Ethernet Controllers support the following modes of operation in a virtualized environment:

• SR-IOV mode: Involves direct assignment of part of the port resources to different guest operating systems using the PCI-SIG Single Root I/O Virtualization (SR IOV) standard, also known as "native mode" or "pass-through" mode.

• VMDq mode: Involves central management of the networking resources by an IO Virtual Machine (IOVM) or a Virtual Machine Monitor (VMM), also known as "software switch acceleration" mode. In this chapter, this mode is referred to as theNext Generation VMDq mode.

A Virtual Function has basic access to the queue resources and control structures of the queues assigned to it. For global resource access, a Virtual Function has to send a request to the Physical Function for that port, and the Physical Function operates on the global resources on behalf of the Virtual Function. For this out-of-band communication, an SR-IOV enabled NIC provides a memory buffer for each Virtual Function, which is called a "Mailbox".

  1. 採用DPDK EAL的應用程序如何運行

Note:The UIO drivers and hugepages must be setup prior to running an application.

The application is linked with the Intel® DPDK target environment's Environmental Abstraction Layer (EAL) library, which provides some options that are generic to every Intel® DPDK application. The following is the list of options that can be given to the EAL:

./rte-app -c COREMASK -n NUM [-b ] [--socket-mem=MB,...]

[-m MB] [-r NUM] [-v] [--file-prefix] [--proc-type ]

The EAL options are as follows:

• -c COREMASK: An hexadecimal bitmask of the cores to run on

• -n NUM: Number of memory channels

• -b : blacklisting of ports; prevent EAL from using

specified PCI device (multiple -b options are allowed)

• --socket-mem: Memory to allocate on specific sockets

• -m MB: Memory to allocate

• -r NUM: Number of memory ranks

• -v: Display version information on startup

• --huge-dir: The directory where hugetlbfs is mounted

• --file-prefix: The prefix text used for hugepage filenames

• --proc-type: The type of process instance

The -c and the -n options are mandatory; the others are optional.

Copy the Intel® DPDK application binary to your target, then run the application as follows:

user@target:~$ ./helloworld -c f -n 4

Note: The --proc-type and --file-prefix EAL options are used for running multiple Intel® DPDK processes.

上面的helloworld程序如何運行在多個cpu上呢?下面是helloworld的main函數:

int main(int argc, char **argv)

{

    int ret;

    unsigned lcore_id;

    ret = rte_eal_init(argc, argv); //解析rte_eal程序傳入的相關參數,比如運行的cpu,內存等

    if (ret < 0)

        rte_panic("Cannot init EAL\n");



     //這裏的slave core由rte_eal程序的-p參數決定的

    RTE_LCORE_FOREACH_SLAVE(lcore_id) {

        rte_eal_remote_launch(lcore_hello, NULL, lcore_id);

    }

    

    lcore_hello(NULL);

    rte_eal_mp_wait_lcore();

    return 0;

}

 

 

  1. DPDK如何管理報文及對jumbo frame的支持

由於DPDK支持巨型幀,沒有采用linux的skb管理方式,而是採用BSD的mbuf(但是沒有找到足夠的理由說採用mbuf要比skb的方式優越在哪裏)。

下面是EAL的mbuf管理結構:

struct rte_mbuf {

    struct rte_mempool *pool;

    void *buf_addr;

    phys_addr_t buf_physaddr;

    uint16_t buf_len;

#ifdef RTE_MBUF_SCATTER_GATHER

    union {

        rte_atomic16_t refcnt_atomic;

        uint16_t refcnt;

    };

#else

    uint16_t refcnt_reserved;

#endif

    uint8_t type;

    uint8_t reserved;

    uint16_t ol_flags;

    union {

        struct rte_ctrlmbuf ctrl; //控制消息mbuf結構

        struct rte_pktmbuf pkt; //數據報文mbuf結構

    };

} __rte_cache_aligned;

struct rte_pktmbuf {

    struct rte_mbuf *next;

    void* data;

    uint16_t data_len;


    uint8_t nb_segs;

    uint8_t in_port;

    uint32_t pkt_len;


    union rte_vlan_macip vlan_macip;

    union {

        uint32_t rss;

        struct {

            uint16_t hash;

            uint16_t id;

        } fdir;

    } hash;

};

從上面的描述報文的mbuf結構來看,要比skb簡單得做。Mbuf要支持jumbo frame,從代碼上來看,似乎對管理報文的機構並沒有特殊的要求(這也是skb可以支持jumbo frame的理由),但是對內存分配,包括mbuf結構和data的內存分配提出要求,所以要求linux必須支持huge page。對huge page的管理主要體現在mem的管理上。

  1. DPDK的應用程序之間如何通信

在RMI的系列芯片中,有一個非常關鍵的設計,那就是FMN(Fast Message Network),所有芯片內部的器件包括IO、CPU、Mem都掛在FMN上,他們之間通信是通過所謂的FMN消息來通信,FMN是一個環狀結構,實際上就是一個通信消息的Buffer ring。

Buffer ring在linux裏面已經支持,2.6.28內核已經支持,後來又改進,支持了lockless buffer ring的設計。

在DPDK裏面提供一個buffer ring的庫,即rte_ring庫,至於運行在EAL上的"應用程序"、"接口驅動"等之間如何通信,可以使用rte_ring庫提供的接口去設計和實現。

Ring Buffer也叫Circular Buffer,是一種緩衝區數據結構,一般用於生產者-消費者模式的程序中,一個或多個生產者不停產生數據,同時一個或多個消費者使用數據,數據使用完這塊緩衝區就可以釋放了。

在通信程序中,經常使用環形緩衝區作爲數據結構來存放通信中發送和接收的數據。環形緩衝區是一個先進先出的循環緩衝區,可以向通信程序提供對緩衝區的互斥訪問。

環形緩衝區的實現原理

環形緩衝區通常有一個讀指針和一個寫指針。讀指針指向環形緩衝區中可讀的數據,寫指針指向環形緩衝區中可寫的緩衝區。通過移動讀指針和寫指針就可以實現緩衝區的數據讀取和寫入。在通常情況下,環形緩衝區的讀用戶僅僅會影響讀指針,而寫用戶僅僅會影響寫指針。如果僅僅有一個讀用戶和一個寫用戶,那麼不需要添加互斥保護機制就可以保證數據的正確性。如果有多個讀寫用戶訪問環形緩衝區,那麼必須添加互斥保護機制來確保多個用戶互斥訪問環形緩衝區。

圖1、圖2和圖3是一個環形緩衝區的運行示意圖。圖1是環形緩衝區的初始狀態,可以看到讀指針和寫指針都指向第一個緩衝區處;圖2是向環形緩衝區中添加了一個數據後的情況,可以看到寫指針已經移動到數據塊2的位置,而讀指針沒有移動;圖3是環形緩衝區進行了讀取和添加後的狀態,可以看到環形緩衝區中已經添加了兩個數據,已經讀取了一個數據。

  1. DPDK提供的幾種應用程序運行模式

從DPDK提供的框架來看,一個應用程序可以通過參數設定運行在哪些core上,多個應用程序同時可以運行在同一個DPDK環境下。

多個應用程序,

  1. 可以是相同的應用程序(即完成相同的事務,比如處理數據報文),也可以是不同的應用程序;

  2. 應用程序之間可以通信,也可以不通信;它們之間通信採用共享內存和隊列

  3. 多個應用程序之間可以是主從關係,也可以是並行關係

  4. 多個應用程序可以運行在相同的多個core上,也可以在不同的多個core上,比如一個應用程序運行在0和1號core上,第二個應用程序運行在0和1號,第三個應用程序運行在1和3號core上

  1. DPDK如何支持multi-chips的

DPDK支持multi-cores基本的框架和思路與V5目前基本上是一致的,但是DPDK還支持多芯片NUMA的模式,又是如何做到的呢?

首先需要搞清楚幾個概念:

CMP、SMP、NUMA、MPP

SMP=CPUs+總線+Mems,CPU之間以同等地位訪問共享內存

NUMA=Node+總線+Node,Node=CPUs+總線+Mems

MPP=NUMA或SMP+IO網絡+NUMA或SMP

所以NUMA各個node之間除了能夠訪問本地內存外,還可以訪問其他node的遠程內存

MPP類似於當前的機框交換機,各個單板之間實際就是MPP的方式,只能訪問本地內存,各個節點之間無法訪問內存

從NUMA的架構來看,似乎CPU在訪問內存的時候是有優先級的,先訪問本地內存,再訪問遠程內存,但是從DPDK的內存管理來看,似乎在系統初始化的時候將所有節點的內存按照節點統一管理,按照NUMA的思路,在內存分配的時候,應該是先從Node本地內存分配,如果本地內存不足,再從其他Node上的內存分配,或者顯式地指定從其他Node的內存分配,代碼實現分析:

void *rte_malloc(const char *type, size_t size, unsigned align)

{

    return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);

}

void *

rte_malloc_socket(const char *type, size_t size, unsigned align, int socket)

{

…

    if (socket == SOCKET_ID_ANY) //socket id就是node id

        socket = malloc_get_numa_socket();

…

    return malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,

            size, align == 0 ? 1 : align);

}

void *malloc_heap_alloc(struct malloc_heap *heap,

        const char *type __attribute__((unused)), size_t size, unsigned align)

{

    …

    if (elem == NULL){//如果沒有內存,則添加,關鍵是看如何添加的

        if ((malloc_heap_add_memzone(heap, size, align)) == 0)

            elem = find_suitable_element(heap, size, align, &prev);

    }

    …

}

malloc_heap_add_memzone()-àrte_memzone_reserve()àrte_memzone_reserve_aligned()àmemzone_reserve_aligned_thread_unsafe()

static const struct rte_memzone *

memzone_reserve_aligned_thread_unsafe(const char *name, uint64_t len,

        int socket_id, unsigned flags, unsigned align)

{

…

    for (i = 0; i < RTE_MAX_MEMSEG; i++) {

        …


        if (socket_id != SOCKET_ID_ANY &&

         socket_id != free_memseg[i].socket_id)//如果不允許從其他socket上遠程分配內存,則

            continue;

}

…

    struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];

    rte_snprintf(mz->name, sizeof(mz->name), "%s", name);

    mz->phys_addr = memseg_physaddr;

    mz->addr = memseg_addr;

    mz->len = requested_len;

    mz->hugepage_sz = free_memseg[memseg_idx].hugepage_sz;

    mz->socket_id = free_memseg[memseg_idx].socket_id;//可以看到socket id可能不是本地的了

}

}

 

  1. 如何用DPDK開發數據通信產品

數據通信產品的核心有兩點:

  1. 設備的管理

  2. 數據報文的高效處理

按照類NP的軟件框架,一般數據通信開發平臺分爲控制平面和數據平面,控制平面負責整個系統的配置和管理,數據平面專門處理業務報文。那麼在DPDK上如何設計呢?

控制平面可以認爲是一個應用程序,數據平面由多個應用程序組成。

數據平面至少有下面幾個應用程序:

  1. 報文接收與發送應用程序,用於從網口接收報文,將報文通過網口發送出去

  2. 報文分發應用程序,用於將從網口收上來的報文按照一定的策略分發到數據處理應用程序(這個不是必須的,如果在初始化的時候已經確定哪些應用程序從網口的哪些隊列中接收和發送報文,則這個工作則沒有必要)

  3. 數據處理應用程序,用於處理接收上來的報文。

數據處理應用程序可以運行在多個core上,同時還可以以對稱方式運行多個數據應用程序。可以想見,這樣做的複雜度很高,同一個數據處理應用程序需要解決多個core的資源競爭問題,還需要考慮多個數據應用程序之間的資源競爭,同一個報文上來後,先是報文分發應用程序將報文分發給某個數據應用程序,數據應用程序接收到這個報文後還需要將此報文分發給某個具體的core去處理,涉及兩次分發策略,顯然這種處理比較複雜,那麼簡單的處理方式是什麼呢?一個數據處理應用程序只是綁定運行在一個core上面,有多少個core處理報文,則運行多少個數據處理應用程序,這樣則相對比較簡單。

下面是一個簡單的模型:

The second example of Intel® DPDK multi-process support demonstrates how a set of processes can run in parallel, with each process performing the same set of packet-processing operations. (Since each process is identical in functionality to the others, we refer to this as symmetric multi-processing, to differentiate it from asymmetric multi-processing - such as a client-server mode of operation seen in the next example, where different processes perform different tasks, yet co-operate to form a packet-processing system.) The following diagram shows the data-flow through the application, using two processes.

For example, to run a set of four symmetric_mp instances, running on lcores 1-4, all performing level-2 forwarding of packets between ports 0 and 1, the following commands can be used (assuming run as root):

# ./build/symmetric_mp -c 2 -n 4 --proc-type=auto -- -p 3 --num-procs=4 --proc-id=0

# ./build/symmetric_mp -c 4 -n 4 --proc-type=auto -- -p 3 --num-procs=4 --proc-id=1

# ./build/symmetric_mp -c 8 -n 4 --proc-type=auto -- -p 3 --num-procs=4 --proc-id=2

# ./build/symmetric_mp -c 10 -n 4 --proc-type=auto -- -p 3 --num-procs=4 --proc-id=3

For the symmetric multi-process example, since all processes work in the same manner, once the hugepage shared memory and the network ports are initialized, it is not necessary to restart all processes if the primary instance dies. Instead, that process can be restarted as a secondary, by explicitly setting the proc-type to secondary on the command line. (All subsequent instances launched will also need this explicitly specified, as auto-detection will detect no primary processes running and therefore attempt to re-initialize shared memory.)

下面是從代碼框架上看上面的模型是如何實現的:

Int main(int argc, char **argv)

{

    …


    signal(SIGINT, print_stats);

    signal(SIGTERM, print_stats);
    ret = rte_eal_init(argc, argv);

    …

    

    proc_type = rte_eal_process_type();

    if (rte_pmd_init_all() < 0)

        rte_exit(EXIT_FAILURE, "Cannot init pmd\n");

    if (rte_eal_pci_probe() < 0)

        rte_exit(EXIT_FAILURE, "Cannot probe PCI\n");

    if (rte_eth_dev_count() == 0)

        rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");

    smp_parse_args(argc, argv);


    mp = (proc_type == RTE_PROC_SECONDARY) ?

            rte_mempool_lookup(_SMP_MBUF_POOL) :

            rte_mempool_create(_SMP_MBUF_POOL, NB_MBUFS, MBUF_SIZE,

                    MBUF_CACHE_SIZE, sizeof(struct rte_pktmbuf_pool_private),

                    rte_pktmbuf_pool_init, NULL,

                    rte_pktmbuf_init, NULL,

                    SOCKET0, 0);

    …

    for(i = 0; i < num_ports; i++){

        if(proc_type == RTE_PROC_PRIMARY)

            if (smp_port_init(ports[i], mp, (uint16_t)num_procs) < 0)

                rte_exit(EXIT_FAILURE, "Error initialising ports\n");

    }


    if (proc_type == RTE_PROC_PRIMARY)

        check_all_ports_link_status((uint8_t)num_ports, (~0x0));



    assign_ports_to_cores();

…

    rte_eal_mp_remote_launch(lcore_main, NULL, CALL_MASTER);

    return 0;

}

static int lcore_main(void *arg __rte_unused)

{

    …


    for (;;) {

        struct rte_mbuf *buf[PKT_BURST];



        for (p = start_port; p < end_port; p++) {

            const uint8_t src = ports[p];

            const uint8_t dst = ports[p ^ 1];

            const uint16_t rx_c = rte_eth_rx_burst(src, q_id, buf, PKT_BURST);

            if (rx_c == 0)

                continue;

            pstats[src].rx += rx_c;



            const uint16_t tx_c = rte_eth_tx_burst(dst, q_id, buf, rx_c);

            pstats[dst].tx += tx_c;

            if (tx_c != rx_c) {

                pstats[dst].drop += (rx_c - tx_c);

                for (i = tx_c; i < rx_c; i++)

                    rte_pktmbuf_free(buf[i]);

            }

        }

    }

}

注意:從前面的分析來看,應用程序有兩種運行方式,一種是一個數據處理應用程序運行在多個core上面,另外一種是一個core上綁定運行一個數據應用程序(都相同),那麼到底採用哪種方式呢?第二種在系統魯棒性方面應該比較第一種好,因爲即使運行在某個core上應用程序異常了,可以重新啓動這個應用程序,只要共享內存沒有破壞,是不會影響其他core上的應用程序繼續運行的。但是這只是一種理想狀況,對於安全設備來說,會話表要想設計成每個數據應用程序獨享的方式不是不可以,那麼則意味着有兩個一個前提條件:流量必須按照5元組分流到各個數據應用程序上面去。則數據分發應用程序是必不可少的。

  1. 案例分析—6wind

6wind提供的系統分爲兩套系統,一套是在Intel的DPDK還未出來之前設計的一套系統,另外一套是在Intel的DPDK基礎上將之前開發的系統。但是核心的數據平面部分基本是一樣的思路。

下面分析6wind在數據平面的解決方案,並同當前ISOS V5框架做對比分析。

  1. 網絡設備框架

目前流行的網絡設備開發平臺框架如下:

V5平臺基本上也是這樣的框架,暫時稱之爲"類NP"框架,實際上是NP推動了這個軟件框架走向合理化和成熟化。

6wind基於這樣的理念,提出了下面三種框架:

V5有點類似於SDS模式,但是又與之不同,V5的Slow Path有兩個,一個是在數據平面,一個是在控制平面(linux)內核的協議棧。從下圖可以看出6wind的Fast Path與Slow Path之間交換都是通過共享內存(在其他地方也提到其他接口,比如FPN、FPS、FPC API,但是估計本質上仍然是共享內存)的方式:

 

V5系統,數據平面與控制平面linux之間通信,大部分是消息通信,有小部分是共享內存(低256M:全局變量、共享內存)。

如果純粹採用共享內存,對於分佈式機框設備的適應能力需要做很多的工作,下面是6wind提出的機框分佈式的解決方案:

上面的解決方案存在什麼問題?主控板與業務板之間通信都是通過控制平面Linux來進行,那麼業務板上,控制平面linux又通過共享內存方式與數據平面的FastPath通信,效率上會比較大的折扣,爲什麼不能通過網口直接與數據平面通信呢?包括報文。

  1. VIRTUAL FAST PATH

6wind提供所謂的虛擬FastPath,估計這個框架纔是基於Intel DPDK,將之前內核部分的Fast Path移植到DPDK上(在用戶態以應用程序的方式運行)。

  1. VNB

6wind在資料中反覆提到VNB(Virtual Network Blocks),似乎主要作用是以插件方式集成到Linux的協議棧或FastPath中去,主要進行的工作:

•Protocol encapsulation

•Feature performance optimization by separation of Kernel/Fast Path packet processing from Control Plane

•Assembly of several protocols in order to provide global solutions

•Driver integration

 

  1. 管理框架

6wind的管理框架可能是系統的最大亮點(其他不敢苟同)。

 

  1. 總結

通過DPDK和6wind的解決方案分析,可以得到下面的一些結論:

  1. DPDK提供了一個很好的開發平臺,解決了基礎的框架結構、內存管理、進程通信、接口管理、資源競爭等關鍵要素

  2. 基於DPDK的數據報文處理,比如Slow Path和Fast Path需要各廠商結合業務情況做開發,但是據說商業版的DPDK提供了完善的數據報文處理

  3. DPDK並非Intel首創,實際上在前期NP、Multicore的實踐經驗的提出了框架,而且從前面分析看出DPDK的框架沒有超出當年RMI、Cavium提出的解決框架,RMI提出的數據平面是在內核空間實現,Cavium的框架是在用戶空間實現,由於Intel受到一些廠商用Cavium芯片較多的影響,採用了用戶空間的解決方案。目前看來用戶空間的解決方案在可調試性、可移植性方面要比內核空間的解決方案好得多

  4. 6wind的多核解決方案,在模塊化方面考慮較多,但估計性能方面會受到比較大的影響,據測試6wind提供的解決方案相對於其他廠商,新建能力差得很多,基本上是3000和3萬的差距,但不得不認可6wind的靈活模塊化解決方案

  1. 附錄

    1. 本子系統用到的縮寫詞、定義和術語

DPDK:Data Plane Development Kit

NUMA:Non-Uniform Memory Access

  1. 參考資料

  1. 軟件包 IntelDPDK.L.1.3.1_7

  2. 《intel-dpdk-getting-started-guide.pdf》

  3. 《intel-dpdk-programmers-guide.pdf》

  4. 《intel-dpdk-sample-applications-user-guide.pdf》

  5. 《6Wind Architecture Overview.pdf》

  6. Intel官方網站

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章