QEMU網絡虛擬化(一):qemu-net 初始化

qemu-net 初始化

初始化流程

main() – vl.c 主函數
 | -> net_client_parse : 解析網絡部分命令行參數 QemuOptsList -> QemuOpts -> QemuOpt對應具體鍵值對
 net_init_clients – net.c : 初始化網絡部分,可能存在多個netdev,依次初始化
  -> net_init_client/net_init_netdev -> net_client_init -> net_client_init1 – net.c
​   -> net_client_init_fun [netdev->type] (netdev, name, peer, errp) – net.c
    -> net_init_tap – tap.c
     -> net_tap_init – tap.c
      | -> tap_open – tap-linux.c : 會打開/dev/net/tun字符設備,獲取文件描述符
      net_init_tap_one – tap.c
       -> net_tap_fd_init – tap.c : 會創建tap口對應的NetClientState的結構
        -> tap_read_poll – tap.c
         -> tap_update_fd_handler – tap.c
          -> qemu_set_fd_handler – iohandler.c
           |-> iohandler_init ->aio_context_new -> g_source_new
​           aio_set_fd_handler : 找到iohandler_ctx中對應AioHander節點賦值io_read和io_write
            -> g_source_add_poll : fd會被加入到source(事件源iohandler_ctx)中,並添加到默認contex中

【g_source_new會關聯aio_source_funcs 到GSource的接口函數(prepare,check,dispatch, finalize )】

數據結構

handler結構關係:
在這裏插入圖片描述

aio_source_funcs 定義

static GSourceFuncs aio_source_funcs = {
    			aio_ctx_prepare,
    			aio_ctx_check,
    			aio_ctx_dispatch,
    			aio_ctx_finalize
			};

NetClientState定義(邏輯連接點,nic、tap 、hub等都有該結構體):

struct NetClientState {
    NetClientInfo *info;
    int link_down;
    QTAILQ_ENTRY(NetClientState) next;
    NetClientState *peer; /* 指向對端邏輯節點,例如創建了virt-net nic網絡接口,該指針會指向對端tap對應的NetClientState,同理,對端也會指向本結構體 */
    NetQueue *incoming_queue; /* 隊列收包相關的 */ 
    char *model;
    char *name;
    char info_str[256];
    unsigned receive_disabled : 1;
    NetClientDestructor *destructor;
    unsigned int queue_index;
    unsigned rxfilter_notify_enabled:1;
    int vring_enable;
    int vnet_hdr_len;
    QTAILQ_HEAD(, NetFilterState) filters;
};

net_tap_fd_init / net_hub_port_new -> qemu_new_net_client -> qemu_net_client_setup -> QTAILQ_INSERT_TAIL(&net_clients, nc, next)
qemu_new_nic -> qemu_net_client_setup -> QTAILQ_INSERT_TAIL(&net_clients, nc, next)
系統會維護一個net_clients全局列表,存放所有的NetClientState單元

nic和tap的關聯

命令行假設如下:

-netdev tap,id=hostnet0,ifname=vnet2,downscript=no,queues=4
-device virtio-net-pci,netdev=hostnet0,id=net0,mac=52:54:00:6b:0d:a1,bus=pci.0,addr=0x3

上面已經提到,解析第一行會對應執行到net_init_netdev分支,後面繼續執行到net_init_tap,部分代碼如下:

int net_init_tap(const Netdev *netdev, const char *name,
                 NetClientState *peer, Error **errp)
{
	....
    /* queues對應上面命令行中設置的隊列數 */
	for (i = 0; i < queues; i++) {
        	/* 會多次打開/dev/net/tun設備,對應多個fd文件 */
            fd = net_tap_init(tap, &vnet_hdr, i >= 1 ? "no" : script,
                              ifname, sizeof ifname, queues > 1, errp);
            if (fd == -1) {
                return -1;
            }
        	....
            /* 會產生和queue相同個數的NetClientState結構體 */
            net_init_tap_one(tap, peer, "tap", name, ifname,
                             i >= 1 ? "no" : script,
                             i >= 1 ? "no" : downscript,
                             vhostfdname, vnet_hdr, fd, &err);
            ....
        }
    ....
}

device實例化並初始化後,會設置屬性netdev,最終會執行到set_netdev函數

/* 該函數會設置VirtIONet.nic_conf */
static void set_netdev(Object *obj, Visitor *v, const char *name,
                       void *opaque, Error **errp)
{
    DeviceState *dev = DEVICE(obj);
    Property *prop = opaque;
    NICPeers *peers_ptr = qdev_get_prop_ptr(dev,prop);/*根據prop找到VirtIONet.nic_conf.peers對應的地址 */
    NetClientState **ncs = peers_ptr->ncs; 
    NetClientState *peers[MAX_QUEUE_NUM];
    ....
    /* 會遍歷net_clients全局列表,找出device中netdev屬性對應的值(此處爲"hostnet0")和 netdev中id屬性對應的值相同的NetClient結構,並賦給peers,函數返回隊列數目,即找到的NetClient個數 */
    queues = qemu_find_net_clients_except(str, peers,
                                          NET_CLIENT_DRIVER_NIC,
                                          MAX_QUEUE_NUM);
    ....
    /* 賦值nic_conf.peers */
    for (i = 0; i < queues; i++) {
		....
        ncs[i] = peers[i];    
        ncs[i]->queue_index = i; /* 設置對端隊列索引,次例對應0,1,2,3 */
    }
    peers_ptr->queues = queues; /* 設置隊列數 */
}

virtio_net_device_realize -> qemu_new_nic -> qemu_net_client_setup

NICState *qemu_new_nic(NetClientInfo *info,
                       NICConf *conf,
                       const char *model,
                       const char *name,
                       void *opaque)
{
    NetClientState **peers = conf->peers.ncs;
    NICState *nic;
    int i, queues = MAX(1, conf->peers.queues);
    assert(info->type == NET_CLIENT_DRIVER_NIC);
    assert(info->size >= sizeof(NICState));
    nic = g_malloc0(info->size + sizeof(NetClientState) * queues);/*創建NICState結構,此處創建了queues個 NetClientState結構 */
    nic->ncs = (void *)nic + info->size;
    nic->conf = conf;
    nic->opaque = opaque;
    for (i = 0; i < queues; i++) {
        qemu_net_client_setup(&nic->ncs[i], info, peers[i], model, name,
                              NULL);
        nic->ncs[i].queue_index = i; /* 設置本端隊列索引 */
    }

    return nic;
}
static void qemu_net_client_setup(NetClientState *nc,
                                  NetClientInfo *info,
                                  NetClientState *peer,
                                  const char *model,
                                  const char *name,
                                  NetClientDestructor *destructor)
{
    ....
    if (peer) {
        assert(!peer->peer);
        nc->peer = peer;
        peer->peer = nc;
    }/* 真正的關聯操作,本端和對端的peer指針互指 */
    QTAILQ_INSERT_TAIL(&net_clients, nc, next);/*nic中對應的NetClientState也會加入到全局net_clients */
    nc->incoming_queue = qemu_new_net_queue(qemu_deliver_packet_iov, nc); /*創建隊列*/
    ....
}

NicState和TapState關係示例,假設是4隊列結構:

 NicState — | — NetClientState <---------- 互爲peer-------- > NetClientState ---- TapState
       | — NetClientState <-----------互爲peer-------- > NetClientState ---- TapState
       | — NetClientState <-----------互爲peer-------- > NetClientState ---- TapState
       | — NetClientState <-----------互爲peer-------- > NetClientState ---- TapState

每個NicState有queues個NetClienState,但是每個TapState 卻只有一個,結構體定義:

typedef struct NICState {
    NetClientState *ncs; /* 定義爲指針,指向NetClientState數組,一對多關係 */
    NICConf *conf;
    void *opaque;
    bool peer_deleted;
} NICState;
typedef struct TAPState {
    NetClientState nc; /* 定義爲結構體,一對一關係 */
    int fd;
    char down_script[1024];
    char down_script_arg[128];
    uint8_t buf[NET_BUFSIZE];
    bool read_poll;
    bool write_poll;
    bool using_vnet_hdr;
    bool has_ufo;
    bool enabled;
    VHostNetState *vhost_net;
    unsigned host_vnet_hdr_len;
    Notifier exit;
} TAPState;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章