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

2023-12-15 23:36
文章标签 源码 解析 qemu virtio

本文主要是介绍QEMU源码全解析 —— virtio(8),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

接前一篇文章:

上一回讲解了virtio balloon相关类所涉及的realize函数,如下表所示:

realize函数parent_dc_realize函数
DeviceClassvirtio_pci_dc_realize
PCIDeviceClassvirtio_pci_realize
VirtioPCIClassvirtio_balloon_pci_realizepci_qdev_realize

本回继续接着讲。

再来回顾一下,virtio balloon PCI代理设备类型的初始化函数virtio_balloon_pci_class_init(),在hw/virtio/virtio-balloon-pci.c中,代码如下:

static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data)
{DeviceClass *dc = DEVICE_CLASS(klass);VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);k->realize = virtio_balloon_pci_realize;set_bit(DEVICE_CATEGORY_MISC, dc->categories);pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BALLOON;pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;pcidev_k->class_id = PCI_CLASS_OTHERS;
}

device_set_realized函数在hw/virtio/virtio-balloon-pci.c中,代码如下:

static void device_set_realized(Object *obj, bool value, Error **errp)
{DeviceState *dev = DEVICE(obj);DeviceClass *dc = DEVICE_GET_CLASS(dev);HotplugHandler *hotplug_ctrl;BusState *bus;NamedClockList *ncl;Error *local_err = NULL;bool unattached_parent = false;static int unattached_count;if (dev->hotplugged && !dc->hotpluggable) {error_setg(errp, QERR_DEVICE_NO_HOTPLUG, object_get_typename(obj));return;}if (value && !dev->realized) {if (!check_only_migratable(obj, errp)) {goto fail;}if (!obj->parent) {gchar *name = g_strdup_printf("device[%d]", unattached_count++);object_property_add_child(container_get(qdev_get_machine(),"/unattached"),name, obj);unattached_parent = true;g_free(name);}hotplug_ctrl = qdev_get_hotplug_handler(dev);if (hotplug_ctrl) {hotplug_handler_pre_plug(hotplug_ctrl, dev, &local_err);if (local_err != NULL) {goto fail;}}if (dc->realize) {dc->realize(dev, &local_err);if (local_err != NULL) {goto fail;}}DEVICE_LISTENER_CALL(realize, Forward, dev);/** always free/re-initialize here since the value cannot be cleaned up* in device_unrealize due to its usage later on in the unplug path*/g_free(dev->canonical_path);dev->canonical_path = object_get_canonical_path(OBJECT(dev));QLIST_FOREACH(ncl, &dev->clocks, node) {if (ncl->alias) {continue;} else {clock_setup_canonical_path(ncl->clock);}}if (qdev_get_vmsd(dev)) {if (vmstate_register_with_alias_id(VMSTATE_IF(dev),VMSTATE_INSTANCE_ID_ANY,qdev_get_vmsd(dev), dev,dev->instance_id_alias,dev->alias_required_for_version,&local_err) < 0) {goto post_realize_fail;}}/** Clear the reset state, in case the object was previously unrealized* with a dirty state.*/resettable_state_clear(&dev->reset);QLIST_FOREACH(bus, &dev->child_bus, sibling) {if (!qbus_realize(bus, errp)) {goto child_realize_fail;}}if (dev->hotplugged) {/** Reset the device, as well as its subtree which, at this point,* should be realized too.*/resettable_assert_reset(OBJECT(dev), RESET_TYPE_COLD);resettable_change_parent(OBJECT(dev), OBJECT(dev->parent_bus),NULL);resettable_release_reset(OBJECT(dev), RESET_TYPE_COLD);}dev->pending_deleted_event = false;if (hotplug_ctrl) {hotplug_handler_plug(hotplug_ctrl, dev, &local_err);if (local_err != NULL) {goto child_realize_fail;}}qatomic_store_release(&dev->realized, value);} else if (!value && dev->realized) {/** Change the value so that any concurrent users are aware* that the device is going to be unrealized** TODO: change .realized property to enum that states* each phase of the device realization/unrealization*/qatomic_set(&dev->realized, value);/** Ensure that concurrent users see this update prior to* any other changes done by unrealize.*/smp_wmb();QLIST_FOREACH(bus, &dev->child_bus, sibling) {qbus_unrealize(bus);}if (qdev_get_vmsd(dev)) {vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev);}if (dc->unrealize) {dc->unrealize(dev);}dev->pending_deleted_event = true;DEVICE_LISTENER_CALL(unrealize, Reverse, dev);}assert(local_err == NULL);return;child_realize_fail:QLIST_FOREACH(bus, &dev->child_bus, sibling) {qbus_unrealize(bus);}if (qdev_get_vmsd(dev)) {vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev);}post_realize_fail:g_free(dev->canonical_path);dev->canonical_path = NULL;if (dc->unrealize) {dc->unrealize(dev);}fail:error_propagate(errp, local_err);if (unattached_parent) {/** Beware, this doesn't just revert* object_property_add_child(), it also runs bus_remove()!*/object_unparent(OBJECT(dev));unattached_count--;}
}

设置virtio PCI代理设备的realize属性时,device_set_realized函数中会首先调用DeviceClass->realize函数指针所指向的函数,也就是virtio_pci_dc_realize函数。代码片段如下:

        if (dc->realize) {dc->realize(dev, &local_err);if (local_err != NULL) {goto fail;}}

virtio_pci_dc_realize函数在hw/virtio/virtio-pci.c中,代码如下:

static void virtio_pci_dc_realize(DeviceState *qdev, Error **errp)
{VirtioPCIClass *vpciklass = VIRTIO_PCI_GET_CLASS(qdev);VirtIOPCIProxy *proxy = VIRTIO_PCI(qdev);PCIDevice *pci_dev = &proxy->pci_dev;if (!(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_PCIE) &&virtio_pci_modern(proxy)) {pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;}vpciklass->parent_dc_realize(qdev, errp);
}

virtio_pci_dc_realize函数中会调用VirtioPCIClass->parent_dc_realize,也就是pci_qdev_realize函数。在pci_qdev_realize函数中会调用PCIDeviceClass的realize函数指针所指向的函数,也就是virtio_pci_realize函数。virtio_pci_realize函数在hw/virtio/virtio-pci.c中,代码如下:

static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
{VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);VirtioPCIClass *k = VIRTIO_PCI_GET_CLASS(pci_dev);bool pcie_port = pci_bus_is_express(pci_get_bus(pci_dev)) &&!pci_bus_is_root(pci_get_bus(pci_dev));if (kvm_enabled() && !kvm_has_many_ioeventfds()) {proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;}/* fd-based ioevents can't be synchronized in record/replay */if (replay_mode != REPLAY_MODE_NONE) {proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;}/** virtio pci bar layout used by default.* subclasses can re-arrange things if needed.**   region 0   --  virtio legacy io bar*   region 1   --  msi-x bar*   region 2   --  virtio modern io bar (off by default)*   region 4+5 --  virtio modern memory (64bit) bar**/proxy->legacy_io_bar_idx  = 0;proxy->msix_bar_idx       = 1;proxy->modern_io_bar_idx  = 2;proxy->modern_mem_bar_idx = 4;proxy->common.offset = 0x0;proxy->common.size = 0x1000;proxy->common.type = VIRTIO_PCI_CAP_COMMON_CFG;proxy->isr.offset = 0x1000;proxy->isr.size = 0x1000;proxy->isr.type = VIRTIO_PCI_CAP_ISR_CFG;proxy->device.offset = 0x2000;proxy->device.size = 0x1000;proxy->device.type = VIRTIO_PCI_CAP_DEVICE_CFG;proxy->notify.offset = 0x3000;proxy->notify.size = virtio_pci_queue_mem_mult(proxy) * VIRTIO_QUEUE_MAX;proxy->notify.type = VIRTIO_PCI_CAP_NOTIFY_CFG;proxy->notify_pio.offset = 0x0;proxy->notify_pio.size = 0x4;proxy->notify_pio.type = VIRTIO_PCI_CAP_NOTIFY_CFG;/* subclasses can enforce modern, so do this unconditionally */memory_region_init(&proxy->modern_bar, OBJECT(proxy), "virtio-pci",/* PCI BAR regions must be powers of 2 */pow2ceil(proxy->notify.offset + proxy->notify.size));if (proxy->disable_legacy == ON_OFF_AUTO_AUTO) {proxy->disable_legacy = pcie_port ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;}if (!virtio_pci_modern(proxy) && !virtio_pci_legacy(proxy)) {error_setg(errp, "device cannot work as neither modern nor legacy mode"" is enabled");error_append_hint(errp, "Set either disable-modern or disable-legacy"" to off\n");return;}if (pcie_port && pci_is_express(pci_dev)) {int pos;uint16_t last_pcie_cap_offset = PCI_CONFIG_SPACE_SIZE;pos = pcie_endpoint_cap_init(pci_dev, 0);assert(pos > 0);pos = pci_add_capability(pci_dev, PCI_CAP_ID_PM, 0,PCI_PM_SIZEOF, errp);if (pos < 0) {return;}pci_dev->exp.pm_cap = pos;/** Indicates that this function complies with revision 1.2 of the* PCI Power Management Interface Specification.*/pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3);if (proxy->flags & VIRTIO_PCI_FLAG_AER) {pcie_aer_init(pci_dev, PCI_ERR_VER, last_pcie_cap_offset,PCI_ERR_SIZEOF, NULL);last_pcie_cap_offset += PCI_ERR_SIZEOF;}if (proxy->flags & VIRTIO_PCI_FLAG_INIT_DEVERR) {/* Init error enabling flags */pcie_cap_deverr_init(pci_dev);}if (proxy->flags & VIRTIO_PCI_FLAG_INIT_LNKCTL) {/* Init Link Control Register */pcie_cap_lnkctl_init(pci_dev);}if (proxy->flags & VIRTIO_PCI_FLAG_INIT_PM) {/* Init Power Management Control Register */pci_set_word(pci_dev->wmask + pos + PCI_PM_CTRL,PCI_PM_CTRL_STATE_MASK);}if (proxy->flags & VIRTIO_PCI_FLAG_ATS) {pcie_ats_init(pci_dev, last_pcie_cap_offset,proxy->flags & VIRTIO_PCI_FLAG_ATS_PAGE_ALIGNED);last_pcie_cap_offset += PCI_EXT_CAP_ATS_SIZEOF;}if (proxy->flags & VIRTIO_PCI_FLAG_INIT_FLR) {/* Set Function Level Reset capability bit */pcie_cap_flr_init(pci_dev);}} else {/** make future invocations of pci_is_express() return false* and pci_config_size() return PCI_CONFIG_SPACE_SIZE.*/pci_dev->cap_present &= ~QEMU_PCI_CAP_EXPRESS;}virtio_pci_bus_new(&proxy->bus, sizeof(proxy->bus), proxy);if (k->realize) {k->realize(proxy, errp);}
}

在这个函数的最后会调用VirtioPCIClass的realize函数指针所指向的函数,也就是virtio_balloon_pci_realize函数。代码片段如下:

static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
{VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);VirtioPCIClass *k = VIRTIO_PCI_GET_CLASS(pci_dev);……if (k->realize) {k->realize(proxy, errp);}
}

这篇关于QEMU源码全解析 —— virtio(8)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/498310

相关文章

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

深度解析Python中递归下降解析器的原理与实现

《深度解析Python中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

深度解析Java @Serial 注解及常见错误案例

《深度解析Java@Serial注解及常见错误案例》Java14引入@Serial注解,用于编译时校验序列化成员,替代传统方式解决运行时错误,适用于Serializable类的方法/字段,需注意签... 目录Java @Serial 注解深度解析1. 注解本质2. 核心作用(1) 主要用途(2) 适用位置3

Java MCP 的鉴权深度解析

《JavaMCP的鉴权深度解析》文章介绍JavaMCP鉴权的实现方式,指出客户端可通过queryString、header或env传递鉴权信息,服务器端支持工具单独鉴权、过滤器集中鉴权及启动时鉴权... 目录一、MCP Client 侧(负责传递,比较简单)(1)常见的 mcpServers json 配置

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱

Maven中生命周期深度解析与实战指南

《Maven中生命周期深度解析与实战指南》这篇文章主要为大家详细介绍了Maven生命周期实战指南,包含核心概念、阶段详解、SpringBoot特化场景及企业级实践建议,希望对大家有一定的帮助... 目录一、Maven 生命周期哲学二、default生命周期核心阶段详解(高频使用)三、clean生命周期核心阶

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

Java Scanner类解析与实战教程

《JavaScanner类解析与实战教程》JavaScanner类(java.util包)是文本输入解析工具,支持基本类型和字符串读取,基于Readable接口与正则分隔符实现,适用于控制台、文件输... 目录一、核心设计与工作原理1.底层依赖2.解析机制A.核心逻辑基于分隔符(delimiter)和模式匹

Java+AI驱动实现PDF文件数据提取与解析

《Java+AI驱动实现PDF文件数据提取与解析》本文将和大家分享一套基于AI的体检报告智能评估方案,详细介绍从PDF上传、内容提取到AI分析、数据存储的全流程自动化实现方法,感兴趣的可以了解下... 目录一、核心流程:从上传到评估的完整链路二、第一步:解析 PDF,提取体检报告内容1. 引入依赖2. 封装

深度解析Python yfinance的核心功能和高级用法

《深度解析Pythonyfinance的核心功能和高级用法》yfinance是一个功能强大且易于使用的Python库,用于从YahooFinance获取金融数据,本教程将深入探讨yfinance的核... 目录yfinance 深度解析教程 (python)1. 简介与安装1.1 什么是 yfinance?