赞
踩
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 网卡 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 方式写入网卡配置空间。
#define VIRTIO_ID_NET 1 /* virtio net */
#define VIRTIO_DEV_ANY_ID 0xffffffff
static 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 函数中会调用 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 驱动与常规的 pci 驱动一样,使用 virtio 网卡的 vendor id + device id 匹配设备,匹配到后执行 virtio_pci_probe 函数。此函数核心流程如下:
创建一个 virtio_pci_device 结构并初始化。
调用 virtio_pci_modern_probe、virtio_pci_legacy_probe 解析 virtio 设备的 capabilities 并初始化相关数据结构,同时也会绑定一个 virtio_config_ops 到 virtio_device 上。新的 virtio_device 的 vendor_id 与 device_id 也会被设置为 virtio 总线内部使用的数据。
调用 register_virtio_device 向 virtio 总线注册一个 virtio 设备,此设备由 virtio_pci_device 中的 virtio_device 结构描述。
register_virtio_device 函数的关键过程如下:
如果设备是 virtio 网卡设备,virtio_net 驱动成功 match,此驱动完成类似网卡驱动初始化的过程,其中访问 virtio 网卡 resource 依赖 virtio-pci probe 中绑定的 virtio_config_ops 进行。
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
在内核 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 这一层进行初始化。有如下几条规则:
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 上层驱动以提供某一类服务。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。