QEMU源码全解析 —— virtio(19)

接前一篇文章:

上回书继续讲解virtio_pci_driver的probe回调函数virtio_pci_probe(),在讲到第5段代码的时候,

    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_legacy_probe和virtio_pci_modern_probe。本回就来对它们进行解析。当然,由于legacy已成过去,因此重点围绕virtio_pci_modern_probe函数进行解析,捎带手地也讲一下virtio_pci_legacy_probe()。为了便于理解,再次贴出两个函数的源码:

  • virtio_pci_legacy_probe

virtio_pci_legacy_probe函数在Linux内核源码/drivers/virtio/virtio_pci_legacy.c中,代码如下:

/* the PCI probing function */
int virtio_pci_legacy_probe(struct virtio_pci_device *vp_dev)
{struct virtio_pci_legacy_device *ldev = &vp_dev->ldev;struct pci_dev *pci_dev = vp_dev->pci_dev;int rc;ldev->pci_dev = pci_dev;rc = vp_legacy_probe(ldev);if (rc)return rc;vp_dev->isr = ldev->isr;vp_dev->vdev.id = ldev->id;vp_dev->vdev.config = &virtio_pci_config_ops;vp_dev->config_vector = vp_config_vector;vp_dev->setup_vq = setup_vq;vp_dev->del_vq = del_vq;return 0;
}
  • virtio_pci_modern_probe

virtio_pci_modern_probe函数在Linux内核源码/drivers/virtio/virtio_pci_modern.c中,代码如下:

/* the PCI probing function */
int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev)
{struct virtio_pci_modern_device *mdev = &vp_dev->mdev;struct pci_dev *pci_dev = vp_dev->pci_dev;int err;mdev->pci_dev = pci_dev;err = vp_modern_probe(mdev);if (err)return err;if (mdev->device)vp_dev->vdev.config = &virtio_pci_config_ops;elsevp_dev->vdev.config = &virtio_pci_config_nodev_ops;vp_dev->config_vector = vp_config_vector;vp_dev->setup_vq = setup_vq;vp_dev->del_vq = del_vq;vp_dev->isr = mdev->isr;vp_dev->vdev.id = mdev->id;return 0;
}

virtio_pci_modern_probe函数中最主要的是调用了vp_modern_probe函数,其在Linux内核源码/drivers/virtio/virtio_pci_modern_dev.c中,代码如下:

/** vp_modern_probe: probe the modern virtio pci device, note that the* caller is required to enable PCI device before calling this function.* @mdev: the modern virtio-pci device** Return 0 on succeed otherwise fail*/
int vp_modern_probe(struct virtio_pci_modern_device *mdev)
{struct pci_dev *pci_dev = mdev->pci_dev;int err, common, isr, notify, device;u32 notify_length;u32 notify_offset;check_offsets();/* We only own devices >= 0x1000 and <= 0x107f: leave the rest. */if (pci_dev->device < 0x1000 || pci_dev->device > 0x107f)return -ENODEV;if (pci_dev->device < 0x1040) {/* Transitional devices: use the PCI subsystem device id as* virtio device id, same as legacy driver always did.*/mdev->id.device = pci_dev->subsystem_device;} else {/* Modern devices: simply use PCI device id, but start from 0x1040. */mdev->id.device = pci_dev->device - 0x1040;}mdev->id.vendor = pci_dev->subsystem_vendor;/* check for a common config: if not, use legacy mode (bar 0). */common = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_COMMON_CFG,IORESOURCE_IO | IORESOURCE_MEM,&mdev->modern_bars);if (!common) {dev_info(&pci_dev->dev,"virtio_pci: leaving for legacy driver\n");return -ENODEV;}/* If common is there, these should be too... */isr = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_ISR_CFG,IORESOURCE_IO | IORESOURCE_MEM,&mdev->modern_bars);notify = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_NOTIFY_CFG,IORESOURCE_IO | IORESOURCE_MEM,&mdev->modern_bars);if (!isr || !notify) {dev_err(&pci_dev->dev,"virtio_pci: missing capabilities %i/%i/%i\n",common, isr, notify);return -EINVAL;}err = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(64));if (err)err = dma_set_mask_and_coherent(&pci_dev->dev,DMA_BIT_MASK(32));if (err)dev_warn(&pci_dev->dev, "Failed to enable 64-bit or 32-bit DMA.  Trying to continue, but this might not work.\n");/* Device capability is only mandatory for devices that have* device-specific configuration.*/device = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_DEVICE_CFG,IORESOURCE_IO | IORESOURCE_MEM,&mdev->modern_bars);err = pci_request_selected_regions(pci_dev, mdev->modern_bars,"virtio-pci-modern");if (err)return err;err = -EINVAL;mdev->common = vp_modern_map_capability(mdev, common,sizeof(struct virtio_pci_common_cfg), 4,0, sizeof(struct virtio_pci_common_cfg),NULL, NULL);if (!mdev->common)goto err_map_common;mdev->isr = vp_modern_map_capability(mdev, isr, sizeof(u8), 1,0, 1,NULL, NULL);if (!mdev->isr)goto err_map_isr;/* Read notify_off_multiplier from config space. */pci_read_config_dword(pci_dev,notify + offsetof(struct virtio_pci_notify_cap,notify_off_multiplier),&mdev->notify_offset_multiplier);/* Read notify length and offset from config space. */pci_read_config_dword(pci_dev,notify + offsetof(struct virtio_pci_notify_cap,cap.length),&notify_length);pci_read_config_dword(pci_dev,notify + offsetof(struct virtio_pci_notify_cap,cap.offset),&notify_offset);/* We don't know how many VQs we'll map, ahead of the time.* If notify length is small, map it all now.* Otherwise, map each VQ individually later.*/if ((u64)notify_length + (notify_offset % PAGE_SIZE) <= PAGE_SIZE) {mdev->notify_base = vp_modern_map_capability(mdev, notify,2, 2,0, notify_length,&mdev->notify_len,&mdev->notify_pa);if (!mdev->notify_base)goto err_map_notify;} else {mdev->notify_map_cap = notify;}/* Again, we don't know how much we should map, but PAGE_SIZE* is more than enough for all existing devices.*/if (device) {mdev->device = vp_modern_map_capability(mdev, device, 0, 4,0, PAGE_SIZE,&mdev->device_len,NULL);if (!mdev->device)goto err_map_device;}return 0;err_map_device:if (mdev->notify_base)pci_iounmap(pci_dev, mdev->notify_base);
err_map_notify:pci_iounmap(pci_dev, mdev->isr);
err_map_isr:pci_iounmap(pci_dev, mdev->common);
err_map_common:pci_release_selected_regions(pci_dev, mdev->modern_bars);return err;
}
EXPORT_SYMBOL_GPL(vp_modern_probe);

实际上在老版本KVM即Linux内核代码中,vp_modern_probe函数中的内容绝大多数是直接放在virtio_pci_modern_probe函数中的,后来才单独封了这样一个函数。

(1)vp_modern_probe首先设置了virtio设备的verdor ID和device ID。代码片段如下:

    /* We only own devices >= 0x1000 and <= 0x107f: leave the rest. */if (pci_dev->device < 0x1000 || pci_dev->device > 0x107f)return -ENODEV;if (pci_dev->device < 0x1040) {/* Transitional devices: use the PCI subsystem device id as* virtio device id, same as legacy driver always did.*/mdev->id.device = pci_dev->subsystem_device;} else {/* Modern devices: simply use PCI device id, but start from 0x1040. */mdev->id.device = pci_dev->device - 0x1040;}mdev->id.vendor = pci_dev->subsystem_vendor;

值得注意的是,virtio PCI代理设备的device iD就是前文书(参见QEMU源码全解析 —— virtio(14))在讲virtio_pci_device_plugged函数(QEMU源码中)时设置的PCI_DEVICE_ID_VIRTIO_10_BASE+VIRTIO_ID_BALLOON,即0x1040+5。

所以,这里virtio设备的device ID(mdev->id.device)就是0x1040+5-0x1040=5,也就代表了VIRTIO_ID_BALLOON。

(2)接下来,调用多次virtio_pci_find_capability函数来发现virtio PCI代理设备的pci capability。代码片段如下:

/* check for a common config: if not, use legacy mode (bar 0). */common = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_COMMON_CFG,IORESOURCE_IO | IORESOURCE_MEM,&mdev->modern_bars);if (!common) {dev_info(&pci_dev->dev,"virtio_pci: leaving for legacy driver\n");return -ENODEV;}/* If common is there, these should be too... */isr = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_ISR_CFG,IORESOURCE_IO | IORESOURCE_MEM,&mdev->modern_bars);notify = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_NOTIFY_CFG,IORESOURCE_IO | IORESOURCE_MEM,&mdev->modern_bars);if (!isr || !notify) {dev_err(&pci_dev->dev,"virtio_pci: missing capabilities %i/%i/%i\n",common, isr, notify);return -EINVAL;}err = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(64));if (err)err = dma_set_mask_and_coherent(&pci_dev->dev,DMA_BIT_MASK(32));if (err)dev_warn(&pci_dev->dev, "Failed to enable 64-bit or 32-bit DMA.  Trying to continue, but this might not work.\n");/* Device capability is only mandatory for devices that have* device-specific configuration.*/device = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_DEVICE_CFG,IORESOURCE_IO | IORESOURCE_MEM,&mdev->modern_bars);

这也是在(QEMU源码)virtio_pci_device_plugged函数中写入到virtio PCI代理设备的配置空间中的,参见QEMU源码全解析 —— virtio(14)和QEMU源码全解析 —— virtio(15)。

(3)virtio_pci_find_capability函数找到所属的PCI BAR,然后写入到virt_pci_device的modern_bars成员中。代码片段如下:

    /* Device capability is only mandatory for devices that have* device-specific configuration.*/device = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_DEVICE_CFG,IORESOURCE_IO | IORESOURCE_MEM,&mdev->modern_bars);

从(QEMU源码)virtio_pci_realize函数中可以知道这个modern_bars是1<<4,如下图所示:

(4)接着,pci_request_selected_regions函数就将virtio PCI代理设备的BAR地址空间保留出来了。代码片段如下:

    err = pci_request_selected_regions(pci_dev, mdev->modern_bars,"virtio-pci-modern");if (err)return err;

(5)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/288536.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

探索数据宇宙之飞船 -- python进阶函数numpy

导读&#xff1a;NumPy以其强大的多维数组对象和广泛的数学函数库著称。这些特性使得NumPy成为不仅在学术研究&#xff0c;也在工业界广泛应用的工具。无论是机器学习算法的开发、数据分析、还是复杂的数学模型的构建&#xff0c;NumPy都扮演着举足轻重的角色。 目录 Numpy简…

凝思虚拟机网络配置

1、输入验证码激活&#xff0c;不激活不能使用网络服务 2、启用网卡 ifconfig eth0 up 3、配置网络 vi /etc/network/interfaces&#xff0c;填写网卡eth0相关信息 auto eth0 allow-hotplug eth0 iface eth0 inet static address 10.1.104.210 netmask 255.255.0.0…

2-负载均衡、反向代理

负载均衡、反向代理 upstream server即上游服务器&#xff0c;指Nginx负载均衡到的处理业务的服务器&#xff0c;也可以称之为real server,即真实处理业务的服务器。 对于负载均衡我们要关心的几个方面如下&#xff1a; 上游服务器配置&#xff1a;使用upstream server配置上…

2023年中国法拍房用户画像和数据分析

法拍房主要平台 法拍房主要平台有3家&#xff0c;分别是阿里、京东和北交互联平台。目前官方认定纳入网络司法拍卖的平台共有7家&#xff0c;其中阿里资产司法拍卖平台的挂拍量最大。 阿里法拍房 阿里法拍房数据显示2017年&#xff0c;全国法拍房9000套&#xff1b;2018年&a…

C++ 图论之树的重心和直径

1. 重心 什么是树的重心&#xff1f; 物理学而言&#xff0c;重心是指地球对物体中每一微小部分引力的合力作用点&#xff0c;物体受力最集中的那一个点。数学上的重心是指三角形的三条中线的交点。 树的重心也称为质点&#xff0c;有一个很官方的定义&#xff1a;如果在树中…

【jvm从入门到实战】(九) 垃圾回收(2)-垃圾回收器

垃圾回收器是垃圾回收算法的具体实现。 由于垃圾回收器分为年轻代和老年代&#xff0c;除了G1之外其他垃圾回收器必须成对组合进行使用 垃圾回收器的组合使用关系图如下。 常用的组合如下: Serial&#xff08;新生代&#xff09; Serial Old&#xff08;老年代&#xff09; Pa…

嵌入式串口输入详细实例

学习目标 掌握串口初始化流程掌握串口输出单个字符掌握串口输出字符串掌握通过串口printf熟练掌握串口开发流程学习内容 需求 串口循环输出内容到PC机。 串口数据发送 添加Usart功能。 首先,选中Firmware,鼠标右键,点击Manage Project Items 接着,将gd32f4xx_usart.c添…

爱心集市,走进黄埔区老人院

为了满足老人的日常生活需求&#xff0c;提升他们的生活质量&#xff0c;将便民服务下沉到黄埔区老人院&#xff0c;让老人能在家门口享受到务实亲切的便民服务。12月14日上午&#xff0c;黄埔区平安促进会联合黄埔区老人院社工部在黄埔区老人院开展“惠捷购&#xff0c;乐生活…

万兆网络之疑难杂症(一)

症状&#xff1a;电话线测线仪4芯全亮&#xff0c;插上话机不亮 由于装修方没有按要求布线&#xff0c;导致没有电话线用&#xff0c;因此分网线用于电话线 测试网线8芯全亮&#xff0c;分四芯用端子接电话线&#xff0c;再压电话线水晶头&#xff0c;再测水晶头全亮&#xf…

【uniapp】uniapp中本地存储sqlite数据库保姆级使用教程(附完整代码和注释)

数据库请求接口封装 uniapp中提供了plus.sqlite接口&#xff0c;在这里我们对常用的数据库请求操作进行了二次封装 这里的dbName、dbPath、recordsTable 可以根据你的需求自己命名 module.exports {/** * type {String} 数据库名称*/dbName: salary,/*** 数据库地址* type {…

R语言生物群落(生态)数据统计分析与绘图丨R语言基础、tidyverse数据清洗、多元统计分析、随机森林模型、回归及混合效应模型、结构方程模型、统计结果作图

R 语言的开源、自由、免费等特点使其广泛应用于生物群落数据统计分析。生物群落数据多样而复杂&#xff0c;涉及众多统计分析方法。本教程以生物群落数据分析中的最常用的统计方法回归和混合效应模型、多元统计分析技术及结构方程等数量分析方法为主线&#xff0c;通过多个来自…

Azure Machine Learning - 提示工程高级技术

本指南将指导你提示设计和提示工程方面的一些高级技术。 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;同济本复旦硕&#xff0c;复旦机器人智能实验室成员&#xff0c;阿里云认证的资深架构师&#xff0c…