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_openlib
はLuaのライブラリを読み込んだり種々の変数を設定したりする。
また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;