Packets are too Fast

slankdevの報告

DPDKアプリをビルドする自前Makefile

DPDKどドキュメントによると、DPDKを使用したアプリケーションでは用意された雛形のMakefileを 一部変更することでビルド環境を提供しているが、自分で何かをつくりたいときにそんなことを やってられない。

ここでは自分で作成したMakefileでDPDKのアプリケーションをビルドする方法をまとめていく。

答えを見つけるまでのアプローチ方法

以下のアプローチで進めていけば一応絶対わかる。

  • オリジナルのビルド手順をすべて確認する
  • makeにnオプションを与えて実行されるコマンドを確認する
  • そのコマンドから少しずついらないオプションを減らしていく
  • どんなDPDKアプリでもビルドできる状態で限界まで減らす
  • 完成

模範解答的Makefile?

一応以上のことからこんなMakefileが一般的だとわかった。

DPDKPATH=$(RTE_SDK)/$(RTE_TARGET)
CXXFLAGS = -I$(DPDKPATH)/include -include $(DPDKPATH)/include/rte_config.h
LIBS = \
    -m64 -pthread -march=native \
    -Wl,--no-as-needed \
    -Wl,-export-dynamic \
    -L$(DPDKPATH)/lib \
    -lpthread -ldl -lrt -lm -lpcap \
    -Wl,--whole-archive \
    -Wl,--start-group \
    -ldpdk \
    -Wl,--end-group \
    -Wl,--no-whole-archive

all:
    g++ $(CXXFLAGS) main.cc $(LIBS)

実行用のサンプルアプリケーション

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>

#include <rte_config.h>
#include <rte_version.h>
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_ether.h>
#include <rte_cycles.h>
#include <rte_lcore.h>
#include <rte_mbuf.h>
#include <rte_hexdump.h>

#define RX_RING_SIZE 128
#define TX_RING_SIZE 512
#define NUM_MBUFS       8191
#define MBUF_CACHE_SIZE 250
#define BURST_SIZE      32

static __attribute((noreturn)) void lcore_main(void)
{
    class test {
        public:
        int a;
        int b;
        void print()
        {
            printf("HELLO DPDK with C++!!!\n");
        }
    };
    test a;
    a.print();
    const uint8_t num_ports = rte_eth_dev_count();

    uint8_t port;
    for (port = 0; port < num_ports; port++) {
        if (rte_eth_dev_socket_id(port) > 0 && rte_eth_dev_socket_id(port) != (int)rte_socket_id())
            printf("WARNING: port %u is on remote NUMA node to "
                    "polling thread. \n\tPerformance will "
                    "not be optimal. \n ", port);
    }
    printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n", rte_lcore_id());

    for (;;) {
        for (port=0; port<num_ports; port++) {
            struct rte_mbuf* bufs[BURST_SIZE];
            const uint16_t num_rx = rte_eth_rx_burst(port, 0, bufs, BURST_SIZE);

            if (unlikely(num_rx == 0))
                continue;

            uint16_t i;
            for (i=0; i<num_rx; i++) {
                rte_pktmbuf_dump(stdout, bufs[i], sizeof(struct rte_mbuf));
                /* rte_hexdump(stdout, "recv packet", */
                /*         rte_pktmbuf_mtod(bufs[i], void*) , */
                /*         rte_pktmbuf_data_len(bufs[i])     ); */
                uint8_t* head = rte_pktmbuf_mtod(bufs[i], uint8_t*);
                memset(head , 0xee, 6);
            }

            const uint16_t num_tx = 0;
            /* const uint16_t num_tx = rte_eth_tx_burst(port, 0, bufs, num_rx); */
            /* printf("Reflect %d packet !! \n", num_rx); */
            if (unlikely(num_tx < num_rx)) {
                uint16_t buf;
                for (buf=num_tx; buf<num_rx; buf++)
                    rte_pktmbuf_free(bufs[buf]);
            }
            printf("\n\n");
        }
    }
}

static int port_init(uint8_t port, struct rte_mempool* mbuf_pool)
{
    struct rte_eth_rxmode rxmode;
    memset(&rxmode, 0, sizeof rxmode);
    rxmode.max_rx_pkt_len = ETHER_MAX_LEN;

    struct rte_eth_conf port_conf_default;
    memset(&port_conf_default, 0, sizeof(port_conf_default));
    port_conf_default.rxmode = rxmode;

    struct rte_eth_conf port_conf = port_conf_default;
    int ret;
    uint16_t q;

    if (port >= rte_eth_dev_count())
        return -1;

    const uint16_t rx_rings = 1;
    const uint16_t tx_rings = 1;
    ret = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
    if (ret != 0)
        return ret;

    for (q=0; q < rx_rings; q++) {
        ret = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE,
                rte_eth_dev_socket_id(port), NULL, mbuf_pool);
        if (ret < 0)
            return ret;
    }

    for (q=0; q < tx_rings; q++) {
        ret = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE,
                rte_eth_dev_socket_id(port), NULL);
        if (ret < 0)
            return ret;
    }

    ret = rte_eth_dev_start(port);
    if (ret < 0)
        return ret;

    struct ether_addr addr;
    rte_eth_macaddr_get(port, &addr);
    printf("Port %u MAC: %02" PRIx8 " %02" PRIx8 " %02" PRIx8
            " %02" PRIx8 " %02" PRIx8 " %02" PRIx8 "\n",
            (unsigned)port,
            addr.addr_bytes[0], addr.addr_bytes[1],
            addr.addr_bytes[2], addr.addr_bytes[2],
            addr.addr_bytes[3], addr.addr_bytes[4]);
    rte_eth_promiscuous_enable(port);
    return 0;
}

int main(int argc, char** argv)
{
    int ret = rte_eal_init(argc, argv);
    if (ret < 0)
        rte_exit(EXIT_FAILURE, "rte_eal_init() failed\n");

    uint32_t num_ports = rte_eth_dev_count();
    printf("%d ports found  \n", num_ports);
    if (num_ports < 1)
        rte_exit(EXIT_FAILURE, "rte_eth_dev_count()");

    struct rte_mempool* mbuf_pool = rte_pktmbuf_pool_create(
            "MBUF_POOL_SLANK", NUM_MBUFS * num_ports,
            MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());

    if (mbuf_pool == NULL)
        rte_exit(EXIT_FAILURE, "rtembuf_pool_create()");

    uint8_t portid;
    for (portid=0; portid < num_ports; portid++) {
        if (port_init(portid, mbuf_pool) != 0)
            rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n", portid);
    }

    if (rte_lcore_count() > 1)
        printf("WARNING: Too many lcores enabled. Only 1 used. \n");

    lcore_main();
}