x**3な人生

基本的にはメモ

pktgenソースコード・リーディングのメモ (1)

はじめに

主にpktgenのパケット生成や受信に興味があったので調べてみた。 まずはメイン関数から調べてみる。 バージョンはpktgen-3.4.9。

main()概要

main()はapp/pktgen-main.cにある。以下おもな手順。

シグナルハンドラの登録

SIGSEGV、SIGHUP、SIGINT、SIGPIPEの4種類を登録する。

        signal(SIGSEGV, sig_handler);
        signal(SIGHUP, sig_handler);
        ...

pktgenオブジェクトの初期化

memset()を用いてpktgenを0で埋める。 次にデフォルト値を入れていく。

        memset(&pktgen, 0, sizeof(pktgen));

        pktgen.flags            = PRINT_LABELS_FLAG;
        pktgen.ident            = 0x1234;
        pktgen.nb_rxd           = DEFAULT_RX_DESC;
        pktgen.nb_txd           = DEFAULT_TX_DESC;
        pktgen.nb_ports_per_page = DEFAULT_PORTS_PER_PAGE;

pktgenはアプリケーション自体の情報を保持するpktgen_t構造体のインスタンスであり、実体はapp/pktgen.hのpktgen_s構造体として定義されている。 pktgenの持つ属性には、CLIの情報を持つcmdlineやLua Stateなどのポインタ、ポートやlcoreなどの情報が含まれる。

/* app/pktgen.h */

typedef struct pktgen_s {
        struct cmdline *cl;     /**< Command Line information pointer */
        void *L;                /**< Lua State pointer */
        char *hostname;         /**< GUI hostname */

        int32_t socket_port;            /**< GUI port number */
        uint32_t blinklist;             /**< Port list for blinking the led */
        uint32_t flags;                 /**< Flag values */
        uint16_t ident;                 /**< IPv4 ident value */
        uint16_t last_row;              /**< last static row of the screen */
        uint16_t nb_ports;              /**< Number of ports in the system */
        uint8_t starting_port;          /**< Starting port to display */
        uint8_t ending_port;            /**< Ending port to display */
        uint8_t nb_ports_per_page;      /**< Number of ports to display per page */

        uint16_t nb_rxd;        /**< Number of receive descriptors */
        uint16_t nb_txd;        /**< Number of transmit descriptors */
        uint16_t portNum;       /**< Current Port number */
        uint16_t port_cnt;      /**< Number of ports used in total */
        uint64_t hz;            /**< Number of events per seconds */

        int (*callout)(void *callout_arg);
        void *callout_arg;

        struct rte_pci_addr blacklist[RTE_MAX_ETHPORTS];
        struct rte_pci_addr portlist[RTE_MAX_ETHPORTS];
        uint8_t *portdesc[RTE_MAX_ETHPORTS];
        uint32_t portdesc_cnt;
        uint32_t blacklist_cnt;

        /* port to lcore mapping */
        l2p_t *l2p;

        port_info_t info[RTE_MAX_ETHPORTS];     /**< Port information */

        lc_info_t core_info[RTE_MAX_LCORE];
        uint16_t core_cnt;
        uint16_t pad0;
        lscpu_t *lscpu;
        char *uname;
        eth_stats_t cumm_rate_totals;   /**< port rates total values */
        uint64_t max_total_ipackets;    /**< Total Max seen input packet rate */
        uint64_t max_total_opackets;    /**< Total Max seen output packet rate */

        pthread_t thread;       /**< Thread structure for Lua server */

        uint64_t counter;       /**< A debug counter */
        uint64_t mem_used;      /**< Display memory used counters per ports */
        uint64_t total_mem_used;/**< Display memory used for all ports */
        int32_t argc;   /**< Number of arguments */
        char *argv[64]; /**< Argument list */
        capture_t capture[RTE_MAX_NUMA_NODES];  /**< Packet capture, 1 struct per socket */
        uint8_t is_gui_running;
} pktgen_t;

lcoreとポートのマッピングを初期化

pktgenではlcoreとポートとの組み合わせを構造体として保持しており、最初にこれを初期化する。

        if ( (pktgen.l2p = l2p_create()) == NULL)
                pktgen_log_panic("Unable to create l2p");

l2p_tはlcoreとポートとのマッピングを保持する構造体で、次の様に定義される。

/* lib/common/l2p.c */

typedef struct {
        volatile uint8_t stop[RTE_MAX_LCORE];
        lobj_t lcores[RTE_MAX_LCORE];
        pobj_t ports[RTE_MAX_ETHPORTS];
        rxtx_t map[MAX_MAP_PORTS][MAX_MAP_LCORES];
} l2p_t;

またdefineの値は以下の様になっている。

/* lib/common/l2p.h */

#define    MAX_MAP_PORTS           (RTE_MAX_ETHPORTS + 1)
#define MAX_MAP_LCORES         (RTE_MAX_LCORE + 1)

画面、ログ、EAL、CLIの初期化

        /* Initialize the screen and logging */
        pktgen_init_log();
        pktgen_cpu_init();

        /* initialize EAL */
        ret = rte_eal_init(argc, argv);
        ...

        if (pktgen_cli_create())
                return -1;

pktgen_cli_create()はapp/cli-functions.cに定義されている。 この関数はさらにlib/cli/cli.cで定義されたcli_create()を呼び出し、そこからcli_init()を呼び出し、実際の初期化を行う。

lua関連の処理

pktgenはLua stateをpktgen.Lにて管理する。 _lua_openlibLuaのライブラリを読み込んだり種々の変数を設定したりする。 またlua_newlib_add()はLua自体に独自に追加拡張された関数(lib/lua/lua-5.3.4.patchにて定義されている)。

        lua_newlib_add(_lua_openlib);
        cli_set_lua_callback(pktgen_lua_dofile);

        /* Open the Lua script handler. */
        if ( (pktgen.L = lua_create_instance()) == NULL) {
                pktgen_log_error("Failed to open Lua pktgen support library");
                return -1;
        }
        cli_set_user_state(pktgen.L);

pktgenのオプション解析

        /* parse application arguments (after the EAL ones) */
        ret = pktgen_parse_args(argc, argv);
        if (ret < 0)
                return -1;

ここは特に興味が無いので省略。

master lcoreの初期化など

マスターのlcoreが他の処理に割り当てられていないかチェックする。

        i = rte_get_master_lcore();
        if (get_lcore_rxcnt(pktgen.l2p, i) || get_lcore_txcnt(pktgen.l2p, i)) {
                cli_printf("*** Error can not use master lcore for a port\n");
                cli_printf("    The master lcore is %d\n", rte_get_master_lcore());
                exit(-1);
        }

あとHZ、画面描画関連、copyrightの表示などを行っていく。

        pktgen.hz = rte_get_timer_hz(); /* Get the starting HZ value. */

        scrn_create_with_defaults(pktgen.flags & ENABLE_THEME_FLAG);

        rte_delay_ms(100);      /* Wait a bit for things to settle. */

        print_copyright(PKTGEN_APP_NAME, PKTGEN_CREATED_BY);

        pktgen_log_info(
                ...

ポートの初期化

pktgen_config_ports()を呼び出す。この関数の内容は別の記事で書く。

他のlcoreの割当

workerスレッドにタスクを与えて起動していく。 pktgen_launch_one_lcore()はapp/pktgen.cで定義されており、 lcoreが担当するポートがRXもしくはTXのいずれか(もしくは両方)を判定して適切な処理を実行させる。

        /* launch per-lcore init on every lcore except master and master + 1 lcores */
        for (i = 0; i < RTE_MAX_LCORE; i++) {
                if ( (i == rte_get_master_lcore()) || !rte_lcore_is_enabled(i) )
                        continue;
                ret = rte_eal_remote_launch(pktgen_launch_one_lcore, NULL, i);
                if (ret != 0)
                        pktgen_log_error("Failed to start lcore %d, return %d", i, ret);
        }

pktgen_launch_one_lcore(app/pktgen.c)の内容。 それぞれのタスクの内容は名前の通り。 詳細は別の記事で書く予定。

int
pktgen_launch_one_lcore(void *arg __rte_unused)
{
        uint8_t lid = rte_lcore_id();

        if (pktgen_has_work())
                return 0;

        rte_delay_ms((lid + 1) * 21);

        switch (get_type(pktgen.l2p, lid)) {
        case RX_TYPE:               pktgen_main_rx_loop(lid);       break;
        case TX_TYPE:               pktgen_main_tx_loop(lid);       break;
        case (RX_TYPE | TX_TYPE):   pktgen_main_rxtx_loop(lid);     break;
        }
        return 0;
}

CLIの描画関連の処理など

最後に描画関連の処理を行ってCLIの待ち受けを開始する。

        /* Disable printing log messages of level info and below to screen, */
        /* erase the screen and start updating the screen again. */
        pktgen_log_set_screen_level(LOG_LEVEL_WARNING);
        scrn_erase(this_scrn->nrows);

        splash_screen(3, 16, PKTGEN_APP_NAME, PKTGEN_CREATED_BY);

        scrn_resume();

        pktgen_clear_display();

        rte_timer_setup();

        if (pktgen.flags & ENABLE_GUI_FLAG) {
                if (!scrn_is_paused() ) {
                        scrn_pause();
                        scrn_cls();
                        scrn_setw(1);
                        scrn_pos(this_scrn->nrows, 1);
                }

                lua_init_socket(pktgen.L,
                                &pktgen.thread,
                                pktgen.hostname,
                                pktgen.socket_port);
#ifdef GUI
                pktgen_gui_main(argc, argv);
#endif
        }

        pktgen_cli_start();

終了化処理

CLIを抜けると終了化処理を実行して終了。

        execute_lua_close(pktgen.L);

        pktgen_stop_running();

        scrn_pause();

        scrn_setw(1);
        scrn_printf(100, 1, "\n");      /* Put the cursor on the last row and do a newline. */
        scrn_destroy();

        /* Wait for all of the cores to stop running and exit. */
        rte_eal_mp_wait_lcore();

        for (i = 0; i < pktgen.nb_ports; i++) {
                rte_eth_dev_stop(i);
                rte_delay_ms(100);
                rte_eth_dev_close(i);
        }

        cli_destroy();

        return 0;