Skip to content

Linux 内核 Virtio Para-virtualization 框架分析文档

目录

  1. 概述
  2. Virtio 核心数据结构
  3. Virtio Ring 实现
  4. Virtio 设备发现与初始化
  5. Virtio 设备状态机
  6. 特征位 (Feature Bits)
  7. Virtqueue 操作流程
  8. 源码位置索引

1. 概述

Virtio 是一个开放标准,定义了不同类型设备与驱动程序之间通信的协议。最初作为 hypervisor 实现的半虚拟化设备标准,现在已可用于任何符合规范的设备(真实或模拟)。

1.1 关键组件

virtio/
├── virtio.c          # 核心框架实现
├── virtio_ring.c     # 虚拟队列Ring实现
├── virtio_pci*.c     # PCI传输实现
├── virtio_mmio.c     # MMIO传输实现
└── virtio_config.c   # 配置接口

1.2 Virtio 架构

+------------------+     +------------------+
|   Guest OS       |     |   Host/Hypervisor|
|                  |     |                  |
| +--------------+ |     | +--------------+ |
| | Virtio Driver| |<--->| | Virtio Device| |
| +--------------+ |     | +--------------+ |
|        |        |     |        |        |
|        v        |     |        v        |
| +--------------+ |     | +--------------+ |
| | Virtqueue    | |     | | Virtqueue    | |
| +--------------+ |     | +--------------+ |
|        |        |     |        |        |
+--------|---------+     +--------|---------+
         |                       |
         v                       v
   Shared Memory (DMA accessible by both)

2. Virtio 核心数据结构

2.1 struct virtio_device

文件: /Users/sphinx/github/linux/include/linux/virtio.h (第 168-189 行)

c
struct virtio_device {
    int index;                      // 设备唯一索引
    bool failed;                    // 失败状态标志
    bool config_core_enabled;       // 配置变化报告启用标志
    bool config_driver_disabled;     // 驱动禁用配置报告
    bool config_change_pending;      // 待处理的配置变更
    spinlock_t config_lock;         // 保护配置变更报告
    spinlock_t vqs_list_lock;       // 保护vqs列表
    struct device dev;              // 底层设备
    struct virtio_device_id id;      // 设备类型标识
    const struct virtio_config_ops *config;     // 配置操作接口
    const struct vringh_config_ops *vringh_config;
    const struct virtio_map_ops *map;
    struct list_head vqs;           // 虚拟队列链表
    VIRTIO_DECLARE_FEATURES(features);  // 特性位
    void *priv;                     // 驱动私有数据
    union virtio_map vmap;
#ifdef CONFIG_VIRTIO_DEBUG
    struct dentry *debugfs_dir;
    u64 debugfs_filter_features[VIRTIO_FEATURES_U64S];
#endif
};

2.2 struct virtqueue

文件: /Users/sphinx/github/linux/include/linux/virtio.h (第 19-44 行)

c
struct virtqueue {
    struct list_head list;          // 设备虚拟队列链表
    void (*callback)(struct virtqueue *vq);  // 缓冲区消费回调
    const char *name;              // 队列名称(调试用)
    struct virtio_device *vdev;     // 所属virtio设备
    unsigned int index;            // 队列序号
    unsigned int num_free;         // 可用缓冲区数量
    unsigned int num_max;          // 设备支持最大数量
    bool reset;                    // 队列是否处于复位状态
    void *priv;                    // 队列实现私有数据
};

2.3 struct virtio_driver

文件: /Users/sphinx/github/linux/include/linux/virtio.h (第 222-264 行)

c
struct virtio_driver {
    struct device_driver driver;    // 底层设备驱动
    const struct virtio_device_id *id_table;  // 支持的设备ID表
    const unsigned int *feature_table;        // 支持的特性位表
    unsigned int feature_table_size;           // 特性位表大小
    const unsigned int *feature_table_legacy; // 遗留模式特性位表
    unsigned int feature_table_size_legacy;   // 遗留特性位表大小
    int (*validate)(struct virtio_device *dev);  // 特性验证
    int (*probe)(struct virtio_device *dev);     // 设备探测
    void (*scan)(struct virtio_device *dev);     // 扫描(可选)
    void (*remove)(struct virtio_device *dev);   // 设备移除
    void (*config_changed)(struct virtio_device *dev);  // 配置变更回调
    int (*freeze)(struct virtio_device *dev);    // 冻结(挂起)
    int (*restore)(struct virtio_device *dev);   // 恢复(Resume)
    int (*reset_prepare)(struct virtio_device *dev);   // 复位准备
    int (*reset_done)(struct virtio_device *dev);       // 复位完成
    void (*shutdown)(struct virtio_device *dev);        // 关闭同步
};

2.4 struct virtio_config_ops

文件: /Users/sphinx/github/linux/include/linux/virtio_config.h (第 112-140 行)

c
struct virtio_config_ops {
    void (*get)(struct virtio_device *vdev, unsigned offset,
                void *buf, unsigned len);           // 读取配置字段
    void (*set)(struct virtio_device *vdev, unsigned offset,
                const void *buf, unsigned len);     // 写入配置字段
    u32 (*generation)(struct virtio_device *vdev);  // 配置代数计数器
    u8 (*get_status)(struct virtio_device *vdev);   // 获取状态字节
    void (*set_status)(struct virtio_device *dev, u8 status);  // 设置状态字节
    void (*reset)(struct virtio_device *vdev);      // 复位设备
    int (*find_vqs)(struct virtio_device *vdev, unsigned int nvqs,
                    struct virtqueue *vqs[], struct virtqueue_info vqs_info[],
                    struct irq_affinity *desc);    // 查找虚拟队列
    void (*del_vqs)(struct virtio_device *);         // 删除虚拟队列
    void (*synchronize_cbs)(struct virtio_device *); // 同步回调
    u64 (*get_features)(struct virtio_device *vdev);    // 获取设备特性
    void (*get_extended_features)(struct virtio_device *vdev, u64 *features);
    int (*finalize_features)(struct virtio_device *vdev); // 完成特性协商
    const char *(*bus_name)(struct virtio_device *vdev);   // 总线名称
    int (*set_vq_affinity)(struct virtqueue *vq, const struct cpumask *cpu_mask);
    const struct cpumask *(*get_vq_affinity)(struct virtio_device *vdev, int index);
    bool (*get_shm_region)(struct virtio_device *vdev,
                           struct virtio_shm_region *region, u8 id);
    int (*disable_vq_and_reset)(struct virtqueue *vq);  // 禁用队列并复位
    int (*enable_vq_after_reset)(struct virtqueue *vq); // 复位后启用队列
};

3. Virtio Ring 实现

3.1 VRing 数据结构 (Split Ring)

文件: /Users/sphinx/github/linux/include/uapi/linux/virtio_ring.h (第 104-163 行)

c
// 描述符结构 - 16字节,描述一个缓冲区
struct vring_desc {
    __virtio64 addr;      // 缓冲区地址(Guest物理地址)
    __virtio32 len;       // 缓冲区长度
    __virtio16 flags;     // 描述符标志
    __virtio16 next;      // 链中下一个描述符索引
};

// 可用环结构 - Guest填充,Host消费
struct vring_avail {
    __virtio16 flags;     // 标志(VRING_AVAIL_F_NO_INTERRUPT)
    __virtio16 idx;       // 环索引
    __virtio16 ring[];    // 可用描述符头数组
};

// 已用环元素结构
struct vring_used_elem {
    __virtio32 id;        // 使用的描述符链起始索引
    __virtio32 len;       // 描述符链总长度(写入的字节)
};

// 已用环结构 - Host填充,Guest消费
struct vring_used {
    __virtio16 flags;     // 标志(VRING_USED_F_NO_NOTIFY)
    __virtio16 idx;       // 环索引
    vring_used_elem_t ring[];  // 已用描述符数组
};

// 完整VRing结构
struct vring {
    unsigned int num;         // 描述符数量
    vring_desc_t *desc;       // 描述符数组
    vring_avail_t *avail;     // 可用环
    vring_used_t *used;       // 已用环
};

3.2 描述符链 (Descriptor Chain)

文件: /Users/sphinx/github/linux/include/uapi/linux/virtio_ring.h (第 37-42 行)

缓冲区类型标志:
- VRING_DESC_F_NEXT (1):    缓冲区通过next字段继续
- VRING_DESC_F_WRITE (2):    缓冲区是写密集型(否则只读)
- VRING_DESC_F_INDIRECT (4): 缓冲区包含描述符列表

** Descriptor Chain 示意图 **:

单个缓冲区链:
+--------+    +--------+    +--------+
| Desc 0 | -> | Desc 1 | -> | Desc 2 | -> NULL
+--------+    +--------+    +--------+
   |             |             |
   v             v             v
+--------+    +--------+    +--------+
| Buffer |    | Buffer |    | Buffer |
|  (S/G) |    |  (S/G) |    |  (S/G) |
+--------+    +--------+    +--------+

间接描述符:
+--------+
| Indirect|
|  Desc   | ---> 包含描述符数组的缓冲区
+--------+

3.3 vring_init - VRing 初始化

文件: /Users/sphinx/github/linux/include/uapi/linux/virtio_ring.h (第 196-204 行)

c
static inline void vring_init(struct vring *vr, unsigned int num, void *p,
                              unsigned long align)
{
    vr->num = num;
    vr->desc = p;
    vr->avail = (struct vring_avail *)((char *)p + num * sizeof(struct vring_desc));
    vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + sizeof(__virtio16)
        + align-1) & ~(align - 1));
}

内存布局:

+-------------------+
| vring_desc[num]  |  描述符数组
+-------------------+
| avail->flags      |  2字节
| avail->idx        |  2字节
| avail->ring[num]  |  2*num 字节
| used_event_idx    |  2字节
+-------------------+  (对齐到align边界)
| used->flags       |  2字节
| used->idx         |  2字节
| used->ring[]      |  8*num 字节
| avail_event_idx   |  2字节
+-------------------+

3.4 vring_size - 计算 VRing 大小

文件: /Users/sphinx/github/linux/include/uapi/linux/virtio_ring.h (第 206-211 行)

c
static inline unsigned vring_size(unsigned int num, unsigned long align)
{
    return ((sizeof(struct vring_desc) * num + sizeof(__virtio16) * (3 + num)
             + align - 1) & ~(align - 1))
        + sizeof(__virtio16) * 3 + sizeof(struct vring_used_elem) * num;
}

3.5 struct vring_virtqueue

文件: /Users/sphinx/github/linux/drivers/virtio/virtio_ring.c (第 106-273 行)

c
struct vring_virtqueue {
    struct virtqueue vq;           // 通用virtqueue

    bool use_map_api;             // 是否使用DMA API
    bool weak_barriers;           // 是否使用弱内存屏障
    bool broken;                  // 队列已损坏
    bool indirect;                // 支持间接描述符
    bool event;                  // 支持avail event idx

    enum vq_layout layout;        // 队列布局类型
    unsigned int free_head;       // 空闲描述符链表头
    struct used_entry {
        u32 id;
        u32 len;
    } batch_last;                // 批处理最后条目(IN_ORDER使用)

    unsigned int num_added;       // 添加的缓冲区计数
    u16 last_used_idx;            // 上次使用的索引
    u16 last_used;                // 上次使用的描述符ID
    bool event_triggered;         // 事件已触发标志

    union {
        struct vring_virtqueue_split split;    // Split Ring数据
        struct vring_virtqueue_packed packed;   // Packed Ring数据
    };

    bool (*notify)(struct virtqueue *vq);  // 通知回调
    bool we_own_ring;                // 是否拥有ring内存
    union virtio_map map;            // DMA映射信息
};

3.6 Packed Ring 结构

文件: /Users/sphinx/github/linux/drivers/virtio/virtio_ring.c (第 135-169 行)

c
struct vring_virtqueue_packed {
    struct {
        unsigned int num;
        struct vring_packed_desc *desc;      // 描述符数组
        struct vring_packed_desc_event *driver;  // 驱动事件
        struct vring_packed_desc_event *device;  // 设备事件
    } vring;

    bool avail_wrap_counter;        // 可用环包装计数器
    u16 avail_used_flags;           // 可用已用标志
    u16 next_avail_idx;             // 下一可用索引
    u16 event_flags_shadow;         // 事件标志影子

    struct vring_desc_state_packed *desc_state;  // 描述符状态
    struct vring_desc_extra *desc_extra;         // 额外描述符信息

    dma_addr_t ring_dma_addr;      // Ring DMA地址
    dma_addr_t driver_event_dma_addr;
    dma_addr_t device_event_dma_addr;
    size_t ring_size_in_bytes;
    size_t event_size_in_bytes;
};

// 打包描述符事件结构
struct vring_packed_desc_event {
    __le16 off_wrap;   // 描述符环变化事件偏移/包装计数器
    __le16 flags;      // 描述符环变化事件标志
};

// 打包描述符
struct vring_packed_desc {
    __le64 addr;       // 缓冲区地址
    __le32 len;        // 缓冲区长度
    __le16 id;         // 描述符ID
    __le16 flags;      // 标志
};

4. Virtio 设备发现与初始化

4.1 驱动注册

文件: /Users/sphinx/github/linux/drivers/virtio/virtio.c (第 449-458 行)

c
int __register_virtio_driver(struct virtio_driver *driver, struct module *owner)
{
    BUG_ON(driver->feature_table_size && !driver->feature_table);
    driver->driver.bus = &virtio_bus;    // 绑定到virtio总线
    driver->driver.owner = owner;

    return driver_register(&driver->driver);
}

使用宏简化注册:

c
#define register_virtio_driver(drv) \
    __register_virtio_driver(drv, THIS_MODULE)

#define module_virtio_driver(__virtio_driver) \
    module_driver(__virtio_driver, register_virtio_driver, \
            unregister_virtio_driver)

4.2 virtio_bus 总线定义

文件: /Users/sphinx/github/linux/drivers/virtio/virtio.c (第 438-447 行)

c
static const struct bus_type virtio_bus = {
    .name  = "virtio",
    .match = virtio_dev_match,           // 设备-驱动匹配
    .dev_groups = virtio_dev_groups,     // 设备属性组
    .uevent = virtio_uevent,            // 热插拔事件
    .probe = virtio_dev_probe,           // 设备探测
    .remove = virtio_dev_remove,         // 设备移除
    .irq_get_affinity = virtio_irq_get_affinity,
    .shutdown = virtio_dev_shutdown,     // 关闭回调
};

4.3 设备-驱动匹配

文件: /Users/sphinx/github/linux/drivers/virtio/virtio.c (第 74-96 行)

c
static int virtio_dev_match(struct device *_dv, const struct device_driver *_dr)
{
    unsigned int i;
    struct virtio_device *dev = dev_to_virtio(_dv);
    const struct virtio_device_id *ids;

    ids = drv_to_virtio(_dr)->id_table;
    for (i = 0; ids[i].device; i++)
        if (virtio_id_match(dev, &ids[i]))
            return 1;
    return 0;
}

static inline int virtio_id_match(const struct virtio_device *dev,
                  const struct virtio_device_id *id)
{
    // 检查设备ID和厂商ID是否匹配
    if (id->device != dev->id.device && id->device != VIRTIO_DEV_ANY_ID)
        return 0;
    return id->vendor == VIRTIO_DEV_ANY_ID || id->vendor == dev->id.vendor;
}

4.4 register_virtio_device - 设备注册

文件: /Users/sphinx/github/linux/drivers/virtio/virtio.c (第 517-573 行)

c
int register_virtio_device(struct virtio_device *dev)
{
    int err;

    dev->dev.bus = &virtio_bus;           // 设置总线
    device_initialize(&dev->dev);

    // 分配唯一设备索引
    err = ida_alloc(&virtio_index_ida, GFP_KERNEL);
    if (err < 0)
        goto out;

    dev->index = err;
    err = dev_set_name(&dev->dev, "virtio%u", dev->index);

    // 初始化OF节点(设备树)
    err = virtio_device_of_init(dev);

    spin_lock_init(&dev->config_lock);
    dev->config_driver_disabled = false;
    dev->config_core_enabled = false;
    dev->config_change_pending = false;

    INIT_LIST_HEAD(&dev->vqs);
    spin_lock_init(&dev->vqs_list_lock);

    // 复位设备(以防之前驱动搞乱了)
    virtio_reset_device(dev);

    // 确认已看到设备
    virtio_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE);

    virtio_debug_device_init(dev);

    // 添加到总线,触发驱动探测
    err = device_add(&dev->dev);
    if (err)
        goto out_of_node_put;

    return 0;
    // ...错误处理
}

4.5 virtio_dev_probe - 设备探测

文件: /Users/sphinx/github/linux/drivers/virtio/virtio.c (第 270-366 行)

c
static int virtio_dev_probe(struct device *_d)
{
    int err, i;
    struct virtio_device *dev = dev_to_virtio(_d);
    struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
    u64 device_features[VIRTIO_FEATURES_U64S];
    u64 driver_features[VIRTIO_FEATURES_U64S];
    u64 driver_features_legacy;

    // 步骤1: 设置DRIVER状态
    virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER);

    // 步骤2: 获取设备支持的特性
    virtio_get_features(dev, device_features);

    // 步骤3: 构建驱动特性位图
    virtio_features_zero(driver_features);
    for (i = 0; i < drv->feature_table_size; i++) {
        unsigned int f = drv->feature_table[i];
        if (!WARN_ON_ONCE(f >= VIRTIO_FEATURES_BITS))
            virtio_features_set_bit(driver_features, f);
    }

    // 步骤4: 计算共同支持特性
    if (virtio_features_test_bit(device_features, VIRTIO_F_VERSION_1)) {
        for (i = 0; i < VIRTIO_FEATURES_U64S; ++i)
            dev->features_array[i] = driver_features[i] & device_features[i];
    } else {
        virtio_features_from_u64(dev->features_array,
                     driver_features_legacy & device_features[0]);
    }

    // 步骤5: 保留传输层特性位
    for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
        if (virtio_features_test_bit(device_features, i))
            __virtio_set_bit(dev, i);

    // 步骤6: 完成特性协商
    err = dev->config->finalize_features(dev);

    // 步骤7: 驱动验证
    if (drv->validate) {
        err = drv->validate(dev);
        if (err)
            goto err;
    }

    // 步骤8: 检查特性OK
    err = virtio_features_ok(dev);
    if (err)
        goto err;

    // 步骤9: 调用驱动probe
    err = drv->probe(dev);
    if (err)
        goto err;

    // 步骤10: 设置DRIVER_OK
    if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK))
        virtio_device_ready(dev);

    if (drv->scan)
        drv->scan(dev);

    virtio_config_core_enable(dev);

    return 0;

err:
    virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED);
    return err;
}

5. Virtio 设备状态机

5.1 状态定义

文件: /Users/sphinx/github/linux/include/uapi/linux/virtio_config.h (第 36-46 行)

c
/* 状态字节定义 - Guest报告进度并同步特性 */
#define VIRTIO_CONFIG_S_ACKNOWLEDGE    1  // 已看到设备
#define VIRTIO_CONFIG_S_DRIVER         2  // 已找到驱动
#define VIRTIO_CONFIG_S_DRIVER_OK      4  // 驱动配置完成
#define VIRTIO_CONFIG_S_FEATURES_OK    8  // 特性协商完成
#define VIRTIO_CONFIG_S_NEEDS_RESET    0x40  // 设备需要复位
#define VIRTIO_CONFIG_S_FAILED         0x80  // 放弃此设备

5.2 状态转换图

                      +-----------------+
                      | 未初始化        |
                      | (设备被发现)    |
                      +-----------------+
                              |
                              v
                      +-----------------+
            +---------| S_ACKNOWLEDGE   |
            |         +-----------------+
            |                   |
            |                   v
            |         +-----------------+
            |         | S_DRIVER        |
            |         | (驱动已绑定)     |
            |         +-----------------+
            |                   |
            |                   v
            |         +-----------------+
            |         | S_DRIVER_OK     |
   失败复位  |         | (驱动就绪)     |
   -------->+|         +-----------------+
            |                   |
            |                   v
            |         +-----------------+
            | +-------| S_FEATURES_OK   |
            | |       | (特性协商完成)  |
            | |       +-----------------+
            | |                 |
            | |     验证失败    v
            | |       +-----------------+
            | +------>| S_FAILED       |<-------+
            |         | (驱动失败)      |        |
            |         +-----------------+        |
            |                                   |
            +-----------------------------------+
                              |
                              v
                      +-----------------+
                      | S_NEEDS_RESET   |
                      | (设备需复位)    |
                      +-----------------+

5.3 virtio_add_status - 添加状态

文件: /Users/sphinx/github/linux/drivers/virtio/virtio.c (第 196-201 行)

c
void virtio_add_status(struct virtio_device *dev, unsigned int status)
{
    might_sleep();
    dev->config->set_status(dev, dev->config->get_status(dev) | status);
}

6. 特征位 (Feature Bits)

6.1 特性位定义

文件: /Users/sphinx/github/linux/include/uapi/linux/virtio_config.h (第 54-121 行)

c
// 传输层特性范围 (保留给具体传输实现)
#define VIRTIO_TRANSPORT_F_START   28
#define VIRTIO_TRANSPORT_F_END     42

// 通用特性位
#ifndef VIRTIO_CONFIG_NO_LEGACY
#define VIRTIO_F_NOTIFY_ON_EMPTY   24  // 环空时通知
#define VIRTIO_F_ANY_LAYOUT        27  // 支持任意描述符布局
#endif

#define VIRTIO_F_VERSION_1         32  // v1.0兼容
#define VIRTIO_F_ACCESS_PLATFORM   33  // 平台DMA访问
#define VIRTIO_F_RING_PACKED       34  // 打包virtqueue布局
#define VIRTIO_F_IN_ORDER          35  // 按顺序使用缓冲区
#define VIRTIO_F_ORDER_PLATFORM     36  // 平台排序内存
#define VIRTIO_F_SR_IOV            37  // 支持SR-IOV
#define VIRTIO_F_NOTIFICATION_DATA 38  // 通知中传递额外数据
#define VIRTIO_F_NOTIF_CONFIG_DATA 39  // 通知配置数据
#define VIRTIO_F_RING_RESET        40  // 支持队列单独复位
#define VIRTIO_F_ADMIN_VQ          41  // 支持管理virtqueue

// Virtqueue特性位
#define VIRTIO_RING_F_INDIRECT_DESC 28  // 支持间接描述符
#define VIRTIO_RING_F_EVENT_IDX    29   // 支持事件索引

6.2 特性位操作

文件: /Users/sphinx/github/linux/include/linux/virtio_config.h (第 225-265 行)

c
// 测试特性位
static inline bool __virtio_test_bit(const struct virtio_device *vdev,
                     unsigned int fbit)
{
    return virtio_features_test_bit(vdev->features_array, fbit);
}

// 设置特性位
static inline void __virtio_set_bit(struct virtio_device *vdev,
                       unsigned int fbit)
{
    virtio_features_set_bit(vdev->features_array, fbit);
}

// 清除特性位
static inline void __virtio_clear_bit(struct virtio_device *vdev,
                     unsigned int fbit)
{
    virtio_features_clear_bit(vdev->features_array, fbit);
}

// 检查设备是否有某特性
static inline bool virtio_has_feature(const struct virtio_device *vdev,
                      unsigned int fbit)
{
    if (fbit < VIRTIO_TRANSPORT_F_START)
        virtio_check_driver_offered_feature(vdev, fbit);

    return __virtio_test_bit(vdev, fbit);
}

6.3 vring_transport_features - 传输层特性

文件: /Users/sphinx/github/linux/drivers/virtio/virtio_ring.c (第 2525-2543 行)

c
void vring_transport_features(struct virtio_device *vdev)
{
    unsigned int i;

    for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++) {
        switch (i) {
        case VIRTIO_RING_F_INDIRECT_DESC:
            if (!virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC))
                break;
            /* 驱动支持间接描述符 */
            break;
        case VIRTIO_RING_F_EVENT_IDX:
            if (!virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX))
                break;
            /* 驱动支持事件索引 */
            break;
        case VIRTIO_F_VERSION_1:
            /* v1.0支持是必须的 */
            break;
        case VIRTIO_F_ACCESS_PLATFORM:
        case VIRTIO_F_RING_PACKED:
        case VIRTIO_F_IN_ORDER:
        case VIRTIO_F_ORDER_PLATFORM:
            /* 这些特性需要传输层支持 */
            break;
        default:
            /* 未知特性位 */
            break;
        }
    }
}

7. Virtqueue 操作流程

7.1 Virtqueue 创建流程

文件: /Users/sphinx/github/linux/drivers/virtio/virtio_ring.c (第 3260-3283 行)

c
struct virtqueue *vring_create_virtqueue(
    unsigned int index,
    unsigned int num,
    unsigned int vring_align,
    struct virtio_device *vdev,
    bool weak_barriers,
    bool may_reduce_num,
    bool context,
    bool (*notify)(struct virtqueue *vq),
    void (*callback)(struct virtqueue *vq),
    const char *name)
{
    union virtio_map map = {.dma_dev = vdev->dev.parent};

    // 根据设备特性选择Packed或Split Ring
    if (virtio_has_feature(vdev, VIRTIO_F_RING_PACKED))
        return vring_create_virtqueue_packed(...);
    return vring_create_virtqueue_split(...);
}

7.2 virtqueue_add - 添加缓冲区

文件: /Users/sphinx/github/linux/drivers/virtio/virtio_ring.c (第 599-792 行)

Split Ring 添加流程:

1. 验证队列状态
   START_USE(vq)
   检查 vq->broken 状态

2. 检查可用空间
   if (vq->vq.num_free < descs_used)
       return -ENOSPC

3. 分配描述符
   - 如果使用间接描述符: alloc_indirect_split()
   - 否则: 使用 free_head 指向的空闲描述符

4. 填充描述符
   for (out_sgs):
       设置 VRING_DESC_F_NEXT 标志
       映射 DMA 地址
   for (in_sgs):
       设置 VRING_DESC_F_WRITE 标志
       映射 DMA 地址

5. 更新 free_head
   - IN_ORDER: free_head += descs_used
   - 非IN_ORDER: free_head = last_desc.next

6. 存储用户数据
   vq->split.desc_state[head].data = data

7. 添加到可用环
   avail = vq->split.avail_idx_shadow & (num - 1)
   vq->split.vring.avail->ring[avail] = head

8. 更新索引
   virtio_wmb()  // 内存屏障
   vq->split.avail_idx_shadow++
   vq->split.vring.avail->idx = avail_idx_shadow
   vq->num_added++

9. 可能触发 kick
   if (vq->num_added == (1 << 16) - 1)
       virtqueue_kick(&vq->vq)

   END_USE(vq)

7.3 virtqueue_get_buf - 获取完成缓冲区

文件: /Users/sphinx/github/linux/drivers/virtio/virtio_ring.c (第 917-972 行)

Split Ring 获取流程:

1. 验证队列状态
   START_USE(vq)
   检查 vq->broken 状态

2. 检查是否有已用缓冲区
   if (!more_used_split(vq))
       return NULL

3. 内存屏障
   virtio_rmb(vq->weak_barriers)

4. 获取已用环入口
   last_used = vq->last_used & (num - 1)
   i = vq->split.vring.used->ring[last_used].id
   len = vq->split.vring.used->ring[last_used].len

5. 验证描述符
   if (i >= num || !vq->split.desc_state[i].data)
       return NULL

6. 获取用户数据
   ret = vq->split.desc_state[i].data

7. 分离描述符
   detach_buf_split(vq, i, ctx)

8. 更新 last_used_idx
   vq->last_used_idx++

9. 更新事件索引(如果需要)
   if (!(avail_flags & VRING_AVAIL_F_NO_INTERRUPT))
       vring_used_event(vq->split.vring) = last_used_idx

10. END_USE(vq)
    return ret

7.4 virtqueue_kick - 通知Host

文件: /Users/sphinx/github/linux/drivers/virtio/virtio_ring.c (第 794-822 行)

c
static bool virtqueue_kick_prepare_split(struct vring_virtqueue *vq)
{
    u16 new, old;
    bool needs_kick;

    START_USE(vq);

    // 确保新可用条目已暴露
    virtio_mb(vq->weak_barriers);

    old = vq->split.avail_idx_shadow - vq->num_added;
    new = vq->split.avail_idx_shadow;
    vq->num_added = 0;

    // 根据事件索引或标志判断是否需要kick
    if (vq->event) {
        needs_kick = vring_need_event(
            vring_avail_event(&vq->split.vring), new, old);
    } else {
        needs_kick = !(vq->split.vring.used->flags &
                VRING_USED_F_NO_NOTIFY);
    }

    END_USE(vq);
    return needs_kick;
}

7.5 vring_interrupt - 中断处理

文件: /Users/sphinx/github/linux/drivers/virtio/virtio_ring.c (第 3225-3258 行)

c
irqreturn_t vring_interrupt(int irq, void *_vq)
{
    struct vring_virtqueue *vq = to_vvq(_vq);

    if (!more_used(vq)) {
        pr_debug("virtqueue callback with nothing new\n");
        return IRQ_NONE;
    }

    if (unlikely(vq->broken))
        return IRQ_HANDLED;

    data_race(vq->event_triggered = true);

    pr_debug("virtqueue callback for %p (%p)\n", vq, vq->vq.callback);

    // 调用驱动注册的回调函数
    if (vq->vq.callback)
        vq->vq.callback(&vq->vq);

    return IRQ_HANDLED;
}

7.6 操作时序图

Guest Driver                          Host Device
    |                                     |
    | ---- virtqueue_add_buf() ---------> |
    |       (填充描述符,更新avail)          |
    |                                     |
    | ---- virtqueue_kick() ------------> |
    |       (发送通知)                     |
    |                                     |
    |         [共享内存操作]                |
    |                                     |
    | <--- vring_interrupt() -------------
    |       (设备使用完缓冲区)              |
    |                                     |
    | ---- virtqueue_get_buf() ---------> |
    |       (获取已用缓冲区)                |
    |                                     |

8. 源码位置索引

8.1 核心文件

文件描述
/drivers/virtio/virtio.cVirtio核心框架
/drivers/virtio/virtio_ring.cVirtqueue Ring实现
/include/linux/virtio.hVirtio核心数据结构
/include/linux/virtio_config.h配置操作接口
/include/linux/virtio_ring.hRing层接口
/include/uapi/linux/virtio_config.h用户态配置定义
/include/uapi/linux/virtio_ring.h用户态Ring定义
/include/uapi/linux/virtio_ids.hVirtio设备ID

8.2 传输实现

文件描述
/drivers/virtio/virtio_pci_common.cPCI传输公共代码
/drivers/virtio/virtio_pci_modern.c现代PCI传输
/drivers/virtio/virtio_pci_legacy.c遗留PCI传输
/drivers/virtio/virtio_mmio.cMMIO传输
/drivers/virtio/virtio_vdpa.cvDPA传输

8.3 关键函数索引

函数位置描述
register_virtio_driver()virtio.c:449注册virtio驱动
register_virtio_device()virtio.c:517注册virtio设备
virtio_dev_probe()virtio.c:270设备探测
virtio_add_status()virtio.c:196添加设备状态
vring_create_virtqueue()virtio_ring.c:3260创建virtqueue
virtqueue_add_split()virtio_ring.c:599添加缓冲区(Split)
virtqueue_add_packed()virtio_ring.c:1615添加缓冲区(Packed)
virtqueue_get_buf_ctx_split()virtio_ring.c:917获取缓冲区(Split)
virtqueue_get_buf_ctx_packed()virtio_ring.c:2161获取缓冲区(Packed)
vring_interrupt()virtio_ring.c:3225中断处理

8.4 Virtio 设备ID

文件: /include/uapi/linux/virtio_ids.h

ID设备类型
1virtio-net
2virtio-block
3virtio-console
4virtio-rng
5virtio-balloon
8virtio-scsi
16virtio-gpu
19virtio-vsock
20virtio-crypto
24virtio-mem
26virtio-fs

附录 A: vring_need_event 逻辑

文件: /include/uapi/linux/virtio_ring.h (第 219-227 行)

c
static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old)
{
    /*
     * 判断当索引从old增加到new_idx时,是否应该触发事件
     * event_idx: 其他 side 期望收到通知的索引
     * new_idx: 新索引
     * old: 旧索引
     */
    return (__u16)(new_idx - event_idx - 1) < (__u16)(new_idx - old);
}

附录 B: 内存屏障操作

文件: /include/linux/virtio_ring.h (第 26-48 行)

c
// 多路复用内存屏障 - 根据weak_barriers选择
static inline void virtio_mb(bool weak_barriers)
{
    if (weak_barriers)
        virt_mb();    // 虚拟化友好的内存屏障
    else
        mb();         // 物理内存屏障
}

// 读屏障
static inline void virtio_rmb(bool weak_barriers)
{
    if (weak_barriers)
        virt_rmb();
    else
        dma_rmb();
}

// 写屏障
static inline void virtio_wmb(bool weak_barriers)
{
    if (weak_barriers)
        virt_wmb();
    else
        dma_wmb();
}

文档生成时间: 2026-04-26基于 Linux 内核源码分析

基于 VitePress 构建