virtio_net 与 virtio-pci 驱动关联浅析

news/2025/2/21 8:43:55/

virtio-pci 驱动映射 virtio common_cfg resource 空间

virtio-pci 获取 comon_cfg 物理空间的函数调用如下:

mdev->common = vp_modern_map_capability(mdev, common,sizeof(struct virtio_pci_common_cfg), 4,0, sizeof(struct virtio_pci_common_cfg),NULL, NULL);

p_modern_map_capability 函数通过访问 pci 配置空间来获取 virtio 相关属性信息,然后执行 iomap 映射 virito resource 空间到内核虚拟地址中,核心代码如下:

  pci_read_config_byte(dev, off + offsetof(struct virtio_pci_cap,bar),&bar);pci_read_config_dword(dev, off + offsetof(struct virtio_pci_cap, offset),&offset);pci_read_config_dword(dev, off + offsetof(struct virtio_pci_cap, length),&length);..............................................p = pci_iomap_range(dev, bar, offset, length);if (!p)

virtio_net 驱动中调用的 virtio_config_ops 方法

virtio_net 驱动通过调用 virtio_config_ops 中实现的不同函数来读写 virtio 网卡 resource 空间,此结构的一个实例如下:

static const struct virtio_config_ops virtio_pci_config_ops = {.get		= vp_get,.set		= vp_set,.generation	= vp_generation,.get_status	= vp_get_status,.set_status	= vp_set_status,.reset		= vp_reset,.find_vqs	= vp_modern_find_vqs,.del_vqs	= vp_del_vqs,.synchronize_cbs = vp_synchronize_vectors,.get_features	= vp_get_features,.finalize_features = vp_finalize_features,.bus_name	= vp_bus_name,.set_vq_affinity = vp_set_vq_affinity,.get_vq_affinity = vp_get_vq_affinity,.get_shm_region  = vp_get_shm_region,.disable_vq_and_reset = vp_modern_disable_vq_and_reset,.enable_vq_after_reset = vp_modern_enable_vq_after_reset,
};

以 vp_set_status 为例,它实际是对 vp_modern_set_status 函数的封装,vp_modern_set_status 函数的实现如下:

void vp_modern_set_status(struct virtio_pci_modern_device *mdev,u8 status)
{struct virtio_pci_common_cfg __iomem *cfg = mdev->common;/** Per memory-barriers.txt, wmb() is not needed to guarantee* that the cache coherent memory writes have completed* before writing to the MMIO region.*/vp_iowrite8(status, &cfg->device_status);
}
EXPORT_SYMBOL_GPL(vp_modern_set_status);

直接调用 iowrite 来读写 mdev 中 common 字段执行的虚拟内存来以 MMIO 方式写入网卡配置空间。

virtio_net 驱动奇怪的 pci_id_table

#define VIRTIO_ID_NET			1 /* virtio net */
#define VIRTIO_DEV_ANY_ID	0xffffffffstatic struct virtio_device_id id_table[] = {{ VIRTIO_ID_NET, VIRTIO_DEV_ANY_ID },{ 0 },
};

此处并不是 virtio 的 device_id + vendor_id的形式,而是内部定义的值,这里的 vendor id 被设置为全 F,表明只通过 device id 来 match 驱动。

virtio-pci probe 时 virtio_config_ops 的绑定过程

virtio-pci 驱动的 probe 函数中会调用 legacy 与 modern 两种 virtio pci 的 probe 函数来注册 virtio_config_ops 到 virtio 设备中,相关代码如下:

if (force_legacy) {rc = virtio_pci_legacy_probe(vp_dev);/* Also try modern mode if we can't map BAR0 (no IO space). */if (rc == -ENODEV || rc == -ENOMEM)rc = virtio_pci_modern_probe(vp_dev);if (rc)goto err_probe;} else {rc = virtio_pci_modern_probe(vp_dev);if (rc == -ENODEV)rc = virtio_pci_legacy_probe(vp_dev);if (rc)goto err_probe;}

virtio-pci 与 virtio_net 如何协作?

virtio-pci 驱动与常规的 pci 驱动一样,使用 virtio 网卡的 vendor id + device id 匹配设备,匹配到后执行 virtio_pci_probe 函数。此函数核心流程如下:

  1. 创建一个 virtio_pci_device 结构并初始化。

  2. 调用 virtio_pci_modern_probevirtio_pci_legacy_probe 解析 virtio 设备的 capabilities 并初始化相关数据结构,同时也会绑定一个 virtio_config_opsvirtio_device 上。新的 virtio_device 的 vendor_id 与 device_id 也会被设置为 virtio 总线内部使用的数据。

  3. 调用 register_virtio_device 向 virtio 总线注册一个 virtio 设备,此设备由 virtio_pci_device 中的 virtio_device 结构描述。

    register_virtio_device 函数的关键过程如下:

    1. 为当前设备分配一个唯一的 id,并使用此 id 制作 virtioXX 的设备名并填充。
    2. 初始化必要的字段后执行 virtio_reset_device 函数 reset virtio 设备,reset 完成后继续调用 virtio_add_status 通知 qemu 后端。
    3. 调用 device_add 将新的设备添加到总线中,此过程会触发 virtio 总线 match 驱动。
  4. 如果设备是 virtio 网卡设备,virtio_net 驱动成功 match,此驱动完成类似网卡驱动初始化的过程,其中访问 virtio 网卡 resource 依赖 virtio-pci probe 中绑定的 virtio_config_ops 进行。

virtio 总线

virtio bus 与其它总线一样会在 /sys/bus 目录中生成相关内容,包含驱动、设备等属性。其下注册的驱动结构 sys 目录示例如下:

.
├── virtio_balloon
│   ├── bind
│   ├── module -> ../../../../module/virtio_balloon
│   ├── uevent
│   ├── unbind
│   └── virtio1 -> ../../../../devices/pci0000:00/0000:00:07.0/virtio1
├── virtio_console
│   ├── bind
│   ├── module -> ../../../../module/virtio_console
│   ├── uevent
│   ├── unbind
│   └── virtio0 -> ../../../../devices/pci0000:00/0000:00:06.0/virtio0
├── virtio_net
│   ├── bind
│   ├── module -> ../../../../module/virtio_net
│   ├── uevent
│   ├── unbind
│   └── virtio2 -> ../../../../devices/pci0000:00/0000:00:08.0/virtio2
├── virtio_rng
│   ├── bind
│   ├── uevent
│   └── unbind
└── virtio_rproc_serial├── bind├── module -> ../../../../module/virtio_console├── uevent└── unbind

virtio 驱动仍旧可以通过写入 bind、unbind 文件来绑定、解绑设备到总线上注册的驱动,只不过设备的标识并非 pci 号,而是 virtioXXX 这种内部的标识,毕竟 virtio bus 是一种独立的总线。

virtio-net 设备驱动绑定示例如下:

[root@openeuler virtio_net]# echo virtio3 > bind
[root@openeuler virtio_net]# ls
bind  module  uevent  unbind  virtio2  virtio3
[root@openeuler virtio_net]# echo virtio3 > unbind 
[root@openeuler virtio_net]# ls 
bind  module  uevent  unbind  virtio2

virtio 设备 sys 目录结构示例如下:

.
├── virtio0 -> ../../../devices/pci0000:00/0000:00:06.0/virtio0
├── virtio1 -> ../../../devices/pci0000:00/0000:00:07.0/virtio1
├── virtio2 -> ../../../devices/pci0000:00/0000:00:08.0/virtio2
└── virtio3 -> ../../../devices/pci0000:00/0000:00:09.0/virtio3

单个设备的内容示例如下:

[root@openeuler virtio3]# cat modalias
virtio:d00000001v00001AF4
[root@openeuler virtio3]# cat ./device 
0x0001
[root@openeuler virtio3]# cat ./vendor 
0x1af4
[root@openeuler virtio3]# cat ./uevent
MODALIAS=virtio:d00000001v00001AF4
[root@openeuler virtio3]# cat ./modalias 
virtio:d00000001v00001AF4
[root@openeuler virtio3]# cat features 
1110010111111111111101010000110010000000000000000000000000000000
[root@openeuler virtio3]# cat status
0x00000001

virtio 总线匹配 virtio 设备的一些特征

在内核 modules.alias 中查询到 virtio 与e1000e 设备的匹配模式如下:

alias pci:v00008086d000010BCsv*sd*bc*sc*i* e1000e
alias pci:v00008086d000010A4sv*sd*bc*sc*i* e1000e
alias pci:v00008086d0000105Fsv*sd*bc*sc*i* e1000e
alias pci:v00008086d0000105Esv*sd*bc*sc*i* e1000e
................................................
alias virtio:d00000005v* virtio_balloon
alias virtio:d00000012v* virtio_input
alias virtio:d00000003v* virtio_console
alias virtio:d00000010v* virtio_gpu
alias virtio:d00000002v* virtio_blk
alias virtio:d00000008v* virtio_scsi
alias virtio:d00000001v* virtio_net
alias virtio:d00000013v* vmw_vsock_virtio_transport

能够看到 virtio 的 alias 中的 v 并没有指定具体的值而是使用 *,表明总线匹配的时候只使用 device_id,而 e1000e 设备的 alias 中严格按照 vendor id + device id 匹配驱动。

对于 virtio 总线而言,virtio device 的 device id 非常重要,它是匹配 virtio 驱动的源数据,此值在 virtio-pci 这一层进行初始化。有如下几条规则:

  1. legacy virtio 设备的 device id 为相应 pci 设备的 subsystem_device 值。
  2. modern virtio 设备的 device id 在对应 pci 设备的 device_id 小于 0x1040 则设置为 pci 设备的 subsystem_device 值,否则为 pci 设备的 device id 减去 0x1040 的值。

virtio bus 匹配设备驱动的代码如下:

static inline int virtio_id_match(const struct virtio_device *dev,const struct virtio_device_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;
}

如上文所述,virtio-net 当 device 相等时,virtio 上层驱动生命vendor 是否相等都会返回 true,符合上面的判断。

总结

使用 virtio 虚拟网卡时,virtio pci 设备会绑定到 virtio-pci 驱动上,virtio-pci 驱动负责构建新的 virtio 设备并挂入到总线中并 match 驱动,此时如果加载了 virtio_et 驱动并支持新创建的 virtio 设备,就会执行 virtio-net 驱动的 probe 完成网络设备的初始化。

virtio-pci 驱动可以看做是一个 virtio bus 底层的驱动,它对接 pci 总线,并创建新的 virtio 设备注入到 virtio 总线中,根据设备的类型 match 不同的 virtio 上层驱动以提供某一类服务。


http://www.ppmy.cn/news/274637.html

相关文章

VFIO代码分析(2)VFIO-PCI驱动1

VFIO-PCI驱动作为PCIE驱动,在原来的PCIE驱动卸载后,加载VFIO-PCI驱动,与内核之前扫描到的PCIE设备匹配,触发vfio_pci_probe(),进行本驱动的初始化。该驱动提供了用户态访问PCIE设备的配置空间/BAR空间/中断等资源接口&…

linux设备模型:pci驱动程序注册过程

一个具有正常探测功能的pci驱动程序应具有基本的pci_driver结构实现,如: static struct pci_driver driver_ops {.name "driver_name", // 驱动名称.id_table pci_ids, // 驱动ids结构.probe pci_probe, // 驱动探测函数.remove pci_remo…

Linux PCI驱动编写

这几天将以前为内核2.6写的驱动移植到了4.1下,在这里记录一下过程,以及从头整理一下linux下pci驱动的编写方法。   以前的驱动没有使用到linux下的probe方法,在4.1内核下成功编译后,一直无法进入中断,因此参考ch36x的…

Linux的PCI驱动介绍(入门)

1. 关键数据结构 PCI设备上有三种地址空间:PCI的I/O空间、PCI的存储空间和PCI的配置空间。CPU可以访问PCI设备上的所有地址空间,其中I/O空间和存储空间提供给设备驱动程序使用,而配置空间则由Linux内核中的PCI初始化代码使用。内核在启动时负…

pci驱动开发详解

一、在了解pic启动开发前,作为开发人员需了解以下内核结构体: struct pci_device_id { __u32 vendor, device;/* Vendor and device ID or PCI_ANY_ID*/ __u32 subvendor, subdevice;/* Subsystem IDs or PCI_ANY_ID */ __u32 class, class_…

pci

PCI学习笔记 (2011-01-25 12:18) 标签: PCI 配置空间 枚举 分类: PowerPC架构 1.PCI设备编号 每一个PCI device都有其unique PFA(PCI Fcntion Address) PFA由 bus number、device number、function number组成。 一条PCI总线支持256个PFA&…

pcie驱动介绍

转载: pcie驱动介绍_fight_onlyfor_you的博客-CSDN博客_pcie驱动 PCIE设备的地址由总线号、设备号和功能号组成,分别称为厂家ID、设备ID和设备类代码 我们可以利用lspci工具了解这些概念。PCI工具集的一部分,下载地址为http://mj.ucw.cz…

FeignClient【问题】Method threw ‘feign.codec.DecodeException‘ exception.也许是最简单的解决方法

1.问题详情 Method threw feign.codec.DecodeException exception. # detail Could not extract response: no suitable HttpMessageConverter found for response type [class java.lang.Object] and content type [text/json;charsetUTF-8]Openfeign版本&#xff1a; <…