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

接前一篇文章:QEMU源码全解析 —— virtio(21)

前几回讲解了virtio驱动的加载。本回开始讲解virtio驱动的初始化。

在讲解virtio驱动的初始化之前,先要介绍virtio配置的函数集合变量virtio_pci_config_ops。实际上前文书也有提到,如下图的右上角:

virtio_pci_config_ops的初始化有两处,分别在Linux内核源码/drivers/virtio/virtio_pci_legacy.c和Linux内核源码/drivers/virtio/virtio_pci_modern.c中。代码分别如下:

  • legacy
static const struct virtio_config_ops virtio_pci_config_ops = {.get		= vp_get,.set		= vp_set,.get_status	= vp_get_status,.set_status	= vp_set_status,.reset		= vp_reset,.find_vqs	= vp_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,
};
  • modern
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,
};

在此以Linux内核源码/drivers/virtio/virtio_pci_modern.c中的virtio_pci_config_ops为例进行讲解。

在前文书讲到的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_config_ops变量被赋值给了virtio_device结构的config成员。struct virtio_device的定义在Linux内核源码/include/linux/virtio.h中,代码如下:

/*** struct virtio_device - representation of a device using virtio* @index: unique position on the virtio bus* @failed: saved value for VIRTIO_CONFIG_S_FAILED bit (for restore)* @config_enabled: configuration change reporting enabled* @config_change_pending: configuration change reported while disabled* @config_lock: protects configuration change reporting* @vqs_list_lock: protects @vqs.* @dev: underlying device.* @id: the device type identification (used to match it with a driver).* @config: the configuration ops for this device.* @vringh_config: configuration ops for host vrings.* @vqs: the list of virtqueues for this device.* @features: the features supported by both driver and device.* @priv: private pointer for the driver's use.*/
struct virtio_device {int index;bool failed;bool config_enabled;bool config_change_pending;spinlock_t config_lock;spinlock_t vqs_list_lock;struct device dev;struct virtio_device_id id;const struct virtio_config_ops *config;const struct vringh_config_ops *vringh_config;struct list_head vqs;u64 features;void *priv;
};

其中的struct virtio_config_ops的定义在Linux内核源码/include/linux/virtio_config.h中,代码如下:

/*** struct virtio_config_ops - operations for configuring a virtio device* Note: Do not assume that a transport implements all of the operations*       getting/setting a value as a simple read/write! Generally speaking,*       any of @get/@set, @get_status/@set_status, or @get_features/*       @finalize_features are NOT safe to be called from an atomic*       context.* @get: read the value of a configuration field*	vdev: the virtio_device*	offset: the offset of the configuration field*	buf: the buffer to write the field value into.*	len: the length of the buffer* @set: write the value of a configuration field*	vdev: the virtio_device*	offset: the offset of the configuration field*	buf: the buffer to read the field value from.*	len: the length of the buffer* @generation: config generation counter (optional)*	vdev: the virtio_device*	Returns the config generation counter* @get_status: read the status byte*	vdev: the virtio_device*	Returns the status byte* @set_status: write the status byte*	vdev: the virtio_device*	status: the new status byte* @reset: reset the device*	vdev: the virtio device*	After this, status and feature negotiation must be done again*	Device must not be reset from its vq/config callbacks, or in*	parallel with being added/removed.* @find_vqs: find virtqueues and instantiate them.*	vdev: the virtio_device*	nvqs: the number of virtqueues to find*	vqs: on success, includes new virtqueues*	callbacks: array of callbacks, for each virtqueue*		include a NULL entry for vqs that do not need a callback*	names: array of virtqueue names (mainly for debugging)*		include a NULL entry for vqs unused by driver*	Returns 0 on success or error status* @del_vqs: free virtqueues found by find_vqs().* @synchronize_cbs: synchronize with the virtqueue callbacks (optional)*      The function guarantees that all memory operations on the*      queue before it are visible to the vring_interrupt() that is*      called after it.*      vdev: the virtio_device* @get_features: get the array of feature bits for this device.*	vdev: the virtio_device*	Returns the first 64 feature bits (all we currently need).* @finalize_features: confirm what device features we'll be using.*	vdev: the virtio_device*	This sends the driver feature bits to the device: it can change*	the dev->feature bits if it wants.*	Note that despite the name this	can be called any number of*	times.*	Returns 0 on success or error status* @bus_name: return the bus name associated with the device (optional)*	vdev: the virtio_device*      This returns a pointer to the bus name a la pci_name from which*      the caller can then copy.* @set_vq_affinity: set the affinity for a virtqueue (optional).* @get_vq_affinity: get the affinity for a virtqueue (optional).* @get_shm_region: get a shared memory region based on the index.* @disable_vq_and_reset: reset a queue individually (optional).*	vq: the virtqueue*	Returns 0 on success or error status*	disable_vq_and_reset will guarantee that the callbacks are disabled and*	synchronized.*	Except for the callback, the caller should guarantee that the vring is*	not accessed by any functions of virtqueue.* @enable_vq_after_reset: enable a reset queue*	vq: the virtqueue*	Returns 0 on success or error status*	If disable_vq_and_reset is set, then enable_vq_after_reset must also be*	set.*/
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 *vdev, u8 status);void (*reset)(struct virtio_device *vdev);int (*find_vqs)(struct virtio_device *, unsigned nvqs,struct virtqueue *vqs[], vq_callback_t *callbacks[],const char * const names[], const bool *ctx,struct irq_affinity *desc);void (*del_vqs)(struct virtio_device *);void (*synchronize_cbs)(struct virtio_device *);u64 (*get_features)(struct virtio_device *vdev);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);
};

再回过头来看一下Linux内核源码/drivers/virtio/virtio_pci_modern.c中的virtio_pci_config_ops,对照着上边 struct virtio_config_ops的定义。

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,
};

virtio_pci_config_ops结构中的成员函数通常是virtio PCI代理设备的IO操作,包括读写virtio PCI代理设备的PIO和MMIO,如get_status和set_status成员对应的vp_get_status函数和vp_set_status函数。分别来看:

  • get_status

根据struct virtio_config_ops中的说明:

@get_status: read the status byte
 *    vdev: the virtio_device
 *    Returns the status byte

get_status的作用是读取状态字节。有一个参数vdev,代表了virtio device。返回值为读取到的状态字节。

get_status所指向的vp_get_status函数也在Linux内核源码/drivers/virtio/virtio_pci_modern.c中,代码如下:

/* config->{get,set}_status() implementations */
static u8 vp_get_status(struct virtio_device *vdev)
{struct virtio_pci_device *vp_dev = to_vp_device(vdev);return vp_modern_get_status(&vp_dev->mdev);
}

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

/** vp_modern_get_status - get the device status* @mdev: the modern virtio-pci device** Returns the status read from device*/
u8 vp_modern_get_status(struct virtio_pci_modern_device *mdev)
{struct virtio_pci_common_cfg __iomem *cfg = mdev->common;return vp_ioread8(&cfg->device_status);
}
EXPORT_SYMBOL_GPL(vp_modern_get_status);
  • set_status

 @set_status: write the status byte
 *    vdev: the virtio_device
 *    status: the new status byte

set_status的作用是写入状态字节。有两个参数:vdev代表了virtio device;status为新的要写入的状态字节。

set_status所指向的vp_set_status函数也在Linux内核源码/drivers/virtio/virtio_pci_modern.c中,代码如下:

static void vp_set_status(struct virtio_device *vdev, u8 status)
{struct virtio_pci_device *vp_dev = to_vp_device(vdev);/* We should never be setting status to 0. */BUG_ON(status == 0);vp_modern_set_status(&vp_dev->mdev, status);
}

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

/** vp_modern_set_status - set status to device* @mdev: the modern virtio-pci device* @status: the status set to device*/
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);

vp_modern_get_status和vp_modern_set_status函数直接读写vp_dev->mdev->common->device_status。从前文书(QEMU源码全解析 —— virtio(14))的讲解可知,vp_dev->common对应的是virtio PCI代理设备第四个BAR表示的地址中的一段空间。

vp_dev->mdev->common的类型为struct virtio_pci_common_cfg,该结构的定义在Linux内核源码/include/uapi/linux/virtio_pci.h中,代码如下:

/* Fields in VIRTIO_PCI_CAP_COMMON_CFG: */
struct virtio_pci_common_cfg {/* About the whole device. */__le32 device_feature_select;	/* read-write */__le32 device_feature;		/* read-only */__le32 guest_feature_select;	/* read-write */__le32 guest_feature;		/* read-write */__le16 msix_config;		/* read-write */__le16 num_queues;		/* read-only */__u8 device_status;		/* read-write */__u8 config_generation;		/* read-only *//* About a specific virtqueue. */__le16 queue_select;		/* read-write */__le16 queue_size;		/* read-write, power of 2. */__le16 queue_msix_vector;	/* read-write */__le16 queue_enable;		/* read-write */__le16 queue_notify_off;	/* read-only */__le32 queue_desc_lo;		/* read-write */__le32 queue_desc_hi;		/* read-write */__le32 queue_avail_lo;		/* read-write */__le32 queue_avail_hi;		/* read-write */__le32 queue_used_lo;		/* read-write */__le32 queue_used_hi;		/* read-write */
};

struct virtio_pci_common_cfg的每一个成员都表示一个virtio PCI代理设备modern MMIO地址空间中对应的值,读写这写成员都会陷入到QEMU中。比如上面的读取或者设置设备状态的device_status成员,其地址从virtio_pci_common_cfg结构开始的偏移20字节处(4+4+4+4+2+2=20),所以读写该地址的时候会陷入到QEMU中,并且地址是virtio设备的common MemoryRegion偏移20字节处。该MemoryRegion对应的回调操作结构是common_ops,类型为MemoryRegionOps。

common_ops在hw/virtio/virtio-pci.c中初始化,代码如下:

static void virtio_pci_modern_regions_init(VirtIOPCIProxy *proxy,const char *vdev_name)
{static const MemoryRegionOps common_ops = {.read = virtio_pci_common_read,.write = virtio_pci_common_write,.impl = {.min_access_size = 1,.max_access_size = 4,},.endianness = DEVICE_LITTLE_ENDIAN,};……
}

回到struct_pci_config_ops。

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,
};

virtio_pci_config_ops的各个函数封装了这些I/O操作,不仅是MMO操作,还有PIO操作。virtio设备可以通过此结构中的各个回调函数来驱动设备。

本回就讲到这里。下一回以virtio balloon设备的初始化过程为例,分析virtio设备的初始化过程,即上一回讲到的virtio驱动初始化设备的过程中的“执行设备相关的初始化操作”一步。

欲知后事如何,且看下回分解。

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

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

相关文章

万字干货-京东零售数据资产能力升级与实践

开篇 京东自营和商家自运营模式,以及伴随的多种运营视角、多种组合计算、多种销售属性等数据维度,相较于行业同等量级,数据处理的难度与复杂度都显著增加。如何从海量的数据模型与数据指标中提升检索数据的效率,降低数据存算的成…

parallels配置centos虚拟环境

parallels Desktop M1/M2芯片Parallels Desktop 19虚拟机安装使用教程(超详细)-CSDN博客 下镜像记得找和mac芯片匹配的 安装就选第一个centos7不要选第二个 安装有问题就选回退重启 parallel desktop 18/19安装centos7.2009教程_parallels desktop 19…

echarts多y轴样式重叠问题

1、主要属性设置 yAxis: [{//y轴1nameTextStyle: {align: "right",padding: 0}},{//y轴2nameTextStyle: {align: "left",padding: 0}},{//y轴3axisLabel: {margin: 50},nameTextStyle: {align: "left",padding: [0, 0, 0, 50]},axisPointer: {l…

Python Web开发记录 Day2:CSS

名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪) 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 二、CSS1、CSS-初始入门①快速了解②CSS应用方式…

如何计算文件哈希值(MD5值)

生成文件hash值的用途 哈希值,即HASH值,是通过对文件内容进行加密运算得到的一组二进制值,主要用途是用于文件校验或签名。正是因为这样的特点,它常常用来判断两个文件是否相同。 比如,从网络上下载某个文件&#xff0…

C++ Primer Plus 笔记(持续更新)

编译器的正解 数据+算法程序 赋值从右向左进行 cin,cout的本质也是对象 类和对象的解释

OpenAI文生视频大模型Sora概述

Sora,美国人工智能研究公司OpenAI发布的人工智能文生视频大模型(但OpenAI并未单纯将其视为视频模型,而是作为“世界模拟器” ),于2024年2月15日(美国当地时间)正式对外发布。 Sora可以根据用户…

Redis中的AOF重写到底是怎么一回事

首先我们知道AOF和RDB都是Redis持久化的方法。RDB是Redis DB,一种二进制数据格式,这样就是相当于全量保存数据快照了。AOF则是保存命令,然后恢复的时候重放命令。 AOF随着时间推移,会越来越大,因为不断往里追加命令。…

哪些行业适合做小程序?零售电商、餐饮娱乐、旅游酒店、教育生活、医疗保健、金融社交、体育健身、房产汽车、企管等,你的行业在其中么?

引言 在当今数字化时代,小程序成为了各行各业快速发展的数字工具之一。它的轻便、灵活的特性使得小程序在多个行业中找到了广泛的应用。本文将探讨哪些行业适合开发小程序,并介绍各行业中小程序的具体应用。 一、零售和电商 在当今数字化的商业环境中&…

C#之WPF学习之路(2)

目录 控件的父类 DispatcherObject类 DependencyObject类 DependencyObject 类的关键成员和方法 Visual类 Visual 类的主要成员和方法 UIElement类 UIElement 类的主要成员和功能 FrameworkElement类 FrameworkElement 类的主要成员和功能 控件的父类 在 WPF (Windo…

谷粒商城篇章9 ---- P248-P261/P292-P294 ---- 消息队列【分布式高级篇六】

目录 1 消息队列(Message Queue)简介 1.1 概述 1.2 消息服务中两个重要概念 1.3 消息队列主要有两种形式的目的地 1.4 JMS和AMQP对比 1.5 应用场景 1.6 Spring支持 1.7 SpringBoot自动配置 1.7 市面上的MQ产品 2 RabbitMQ 2.1 RabbitMQ简介 2.1.1 RabbitMQ简介 2…

什么是Elasticsearch SQL

什么是Elasticsearch SQL 一. 介绍二. SQL 入门 前言 这是我在这个网站整理的笔记,有错误的地方请指出,关注我,接下来还会持续更新。 作者:神的孩子都在歌唱 一. 介绍 Elasticsearch SQL 是一个 X-Pack 组件,允许针对 Elasticsea…

通俗易懂理解G-GhostNet轻量级神经网络模型

一、参考资料 原始论文:[1] IJCV22 | 已开源 | 华为GhostNet再升级,全系列硬件上最优极简AI网络 二、G-GhostNet相关介绍 G-GhostNet 又称为 GhostNetV1 的升级版,是针对GPU优化的轻量级神经网络。 1. 摘要 GhostNetV1 作为近年来最流行…

Leetcode 611.有效三角形的个数

题目 给定一个包含非负整数的数组 nums &#xff0c;返回其中可以组成三角形三条边的三元组个数。 示例 1: 输入: nums [2,2,3,4] 输出: 3 解释:有效的组合是: 2,3,4 (使用第一个 2) 2,3,4 (使用第二个 2) 2,2,3示例 2: 输入: nums [4,2,3,4] 输出: 4提示: 1 < nums…

WPF 开发调试比较:Visual Studio 原生和Snoop调试控制台

文章目录 前言运行环境简单的WPF代码实现一个简单的ListBoxVisual Studio自带代码调试热重置功能测试实时可视化树查找窗口元素显示属性 Snoop调试使用Snoop简单使用调试控制台元素追踪结构树Visual/可视化结构树Logical/本地代码可视化树AutoMation/自动识别结构树 WPF元素控制…

基于springboot+vue的房屋租赁管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

手拉手Vite+Vue3+TinyVue+Echarts+TailwindCSS

技术栈springboot3hutool-alloshi-coreVue3viteTinyVueEchartsTailwindCSS软件版本IDEAIntelliJ IDEA 2022.2.1JDK17Spring Boot3.1hutool-all5.8.18oshi-core6.4.1Vue35.0.10vite5.0.10axios1.6.7echarts5.4.3 ECharts是一个使用 JavaScript 实现的开源可视化库&#xff0c;可…

go-zero微服务入门教程

go-zero微服务入门教程 本教程主要模拟实现用户注册和用户信息查询两个接口。 准备工作 安装基础环境 安装etcd&#xff0c; mysql&#xff0c;redis&#xff0c;建议采用docker安装。 MySQL安装好之后&#xff0c;新建数据库dsms_admin&#xff0c;并新建表sys_user&#…

详细分析Python中的unittest测试框架

目录 1. 基本知识2. API2.1 断言2.2 setUp() 和 tearDown() 3. Demo 1. 基本知识 unittest 是 Python 标准库中的一个单元测试框架&#xff0c;用于编写和执行测试用例以验证代码的正确性 提供了一种结构化的方法来编写测试&#xff0c;使得测试代码更加模块化和易于维护 以…

【ACW 服务端】页面操作Java增删改查代码生成

版本: 1.2.2-JDK17-SNAPSHOT 项目地址&#xff1a;wu-smart-acw 演示地址&#xff1a;演示地址 admin/admin Java增删改查代码生成 找到对应菜单 选择你需要的数据实例 选择数据库 选择数据库表 选择客户端&#xff08;如果是本地ACW服务代码启动默认注册上的客户端ID是…