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;