Virtio 驱动初始化数据收发流程详解
Virtio 驱动核心流程详解
一、整体架构概览
二、初始化流程
1. 核心步骤
2. 关键函数调用栈
virtnet_probe()├─ virtio_cread_feature() // 特性协商├─ alloc_etherdev_mq() // 创建net_device├─ virtnet_find_vqs() // 初始化virtqueues│ ├─ virtio_find_vqs()│ └─ virtqueue_set_affinity()├─ register_netdev() // 注册网络设备└─ virtnet_alloc_rx_buffers() // 预填充接收缓冲区
三、数据发送流程(TX)
1. 核心流程
2. 关键函数
// 发送入口
start_xmit(struct sk_buff *skb)├─ skb_to_sgvec() // 创建分散/聚集列表├─ virtqueue_add_outbuf() // 添加发送描述符└─ virtqueue_kick() // 通知设备// 发送完成处理
skb_xmit_done(struct virtqueue *vq)├─ virtqueue_get_buf() // 获取完成描述符├─ dma_unmap_single() // 解除DMA映射└─ dev_consume_skb_any() // 释放sk_buff
四、数据接收流程(RX)
1. 核心流程
2. 关键函数
// 中断处理
virtnet_interrupt()└─ napi_schedule() // 调度NAPI轮询// 接收处理
virtnet_poll(struct napi_struct *napi)├─ virtqueue_get_buf() // 获取接收描述符├─ virtnet_receive_skb() // 构造sk_buff├─ napi_gro_receive() // 提交协议栈└─ virtqueue_add_inbuf() // 补充新缓冲区
五、关键数据结构
1. virtio_device
struct virtio_device {struct device dev; // 设备模型struct virtio_device_id id; // 设备标识struct virtio_config_ops *config; // 配置操作struct list_head vqs; // virtqueue列表u64 features; // 协商特性void *priv; // 驱动私有数据
};
2. virtqueue
struct virtqueue {struct list_head list; // 设备链表void (*callback)(struct virtqueue *vq); // 回调函数const char *name; // 队列名称struct virtio_device *vdev; // 关联设备unsigned int index; // 队列索引void *priv; // 私有数据struct vring vring; // 虚拟环结构
};
3. vring (核心通信结构)
struct vring {unsigned int num; // 描述符数量struct vring_desc *desc; // 描述符表struct vring_avail *avail; // 可用环struct vring_used *used; // 已用环
};
4. virtnet_info (网络设备私有数据)
struct virtnet_info {struct virtio_device *vdev; // virtio设备struct net_device *dev; // 网络设备struct virtqueue *rvq, *svq; // 接收/发送队列struct receive_queue *rq; // 接收队列数组struct send_queue *sq; // 发送队列数组u16 curr_queue_pairs; // 活动队列对struct bpf_prog __rcu *xdp_prog; // XDP程序
};
六、性能优化机制
1. 批处理优化
2. NAPI机制
// 混合中断与轮询
static int virtnet_poll(struct napi_struct *napi, int budget)
{while (packets < budget && (buf = virtqueue_get_buf(vq, &len))) {// 处理数据包packets++;}if (packets < budget) {napi_complete(napi); // 退出轮询模式virtqueue_enable_cb(vq); // 启用回调}return packets;
}
3. 零拷贝技术
七、错误处理机制
1. 队列满处理
netdev_tx_t start_xmit(...)
{if (virtqueue_full(vq)) {netif_stop_queue(dev); // 停止协议栈入队return NETDEV_TX_BUSY; // 通知重试}// ...正常发送...
}// 在完成中断中恢复
void skb_xmit_done(...)
{if (netif_queue_stopped(dev) && !virtqueue_full(vq)) {netif_wake_queue(dev); // 唤醒协议栈}
}
2. 设备重置恢复
static void virtnet_reset(struct virtio_device *vdev)
{// 1. 停止所有数据传输virtnet_freeze_down(vdev);// 2. 重置设备状态virtio_reset_device(vdev);// 3. 重新协商特性virtio_negotiate_features(vdev);// 4. 重建virtqueuesvirtnet_del_vqs(vi);virtnet_find_vqs(vi);// 5. 恢复数据传输virtnet_restore_up(vdev);
}
八、多队列支持
1. 配置流程
2. 数据分发
// 发送队列选择
u16 virtnet_select_queue(struct net_device *dev, struct sk_buff *skb)
{// 1. XDP优先if (vi->xdp_prog)return vi->curr_queue_pairs;// 2. 使用RSS哈希return skb_get_hash(skb) % vi->curr_queue_pairs;
}// 接收中断绑定
static irqreturn_t virtnet_interrupt(int irq, void *dev_id)
{struct receive_queue *rq = dev_id;napi_schedule(&rq->napi); // 调度对应队列的NAPI
}
九、XDP支持
1. 工作流程
2. 关键实现
static struct sk_buff *virtnet_run_xdp(...)
{struct bpf_prog *xdp_prog = rcu_dereference(rq->xdp_prog);xdp_prepare_buff(xdp, buf, offset, len);act = bpf_prog_run_xdp(xdp_prog, xdp);switch (act) {case XDP_PASS:return build_skb(...);case XDP_TX:xdpf = convert_to_xdp_frame(xdp);virtnet_xdp_xmit(dev, 1, &xdpf, 0);break;case XDP_REDIRECT:xdp_do_redirect(dev, xdp, xdp_prog);break;}
}
十、总结:Virtio核心优势
-
标准化接口:
- 统一的前后端通信协议
- 支持多种传输机制(PCI、MMIO、channel)
-
高性能设计:
- 零拷贝数据传输
- 批处理与延迟通知
- 多队列并行处理
-
灵活扩展性:
- 特性协商机制
- 可扩展的virtqueue设计
- 支持现代功能(XDP、TLS offload)
-
跨平台支持:
- 支持KVM、QEMU、Docker等环境
- 兼容多种客户机OS(Linux、Windows)
-
生产级可靠性:
- 完善的错误恢复机制
- 热升级支持
- 实时监控接口
Virtio通过将虚拟设备标准化,在保持高性能的同时,显著降低了虚拟化环境中I/O栈的复杂度,成为现代云计算基础设施的核心组件。其设计思想也影响了物理设备驱动的发展,推动了零拷贝、多队列等技术在物理网卡中的广泛应用。