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.pswp.cn/news/696962.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/696962.shtml
英文地址,請注明出處:http://en.pswp.cn/news/696962.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

c# HttpCookie操作,建立cookie工具類

HttpCookie 是一個在.NET Framework中用于管理和操作HTTP Cookie的類。它提供了一種方便的方式來創建、設置、讀取和刪除Cookie。 Cookie是一種在客戶端和服務器之間傳遞數據的機制,用于跟蹤用戶的會話狀態和存儲用戶相關的信息。它通常由服務器發送給客戶端&#…

萬字干貨-京東零售數據資產能力升級與實踐

開篇 京東自營和商家自運營模式,以及伴隨的多種運營視角、多種組合計算、多種銷售屬性等數據維度,相較于行業同等量級,數據處理的難度與復雜度都顯著增加。如何從海量的數據模型與數據指標中提升檢索數據的效率,降低數據存算的成…

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應用方式…

【C語言】sizeof()函數

前言 sizeof函數用于獲取數據類型或變量在內存中所占的字節數。 sizeof函數返回的是編譯時確定的值,不會計算動態分配的內存大小。 sizeof函數可以用于多種類型的數據,包括數組、指針、結構體、枚舉等。 1.數組 int arr[5];printf("%zu ", siz…

文件上傳與下載

文件上傳與下載 1. 文件上傳 為了能上傳文件,必須將表單的 method 設置為 POST,并將 enctype 設置為 multipart/form-data 。 有兩種實現文件上傳的方式: 底層使用 Apache Commons FileUpload 包 底層使用 Servlet 3.1 內置的文件上傳功能…

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

生成文件hash值的用途 哈希值,即HASH值,是通過對文件內容進行加密運算得到的一組二進制值,主要用途是用于文件校驗或簽名。正是因為這樣的特點,它常常用來判斷兩個文件是否相同。 比如,從網絡上下載某個文件&#xff0…

MySQL主從同步

MySQL主從同步(復制)是一種數據復制技術,用于將數據從一個MySQL數據庫(稱為“主”)復制到另一個或多個MySQL數據庫(稱為“從”)。這個過程通常用于負載均衡、數據備份、災難恢復和其他類似場景。…

C++ Primer Plus 筆記(持續更新)

編譯器的正解 數據+算法程序 賦值從右向左進行 cin,cout的本質也是對象 類和對象的解釋

centerOS docker搭建flowable,流程引擎

1、準備一個mysql數據庫,庫名為flowable 2、mysql驅動下載,下載地址為: https://mvnrepository.com/artifact/mysql/mysql-connector-java此處使用的是8.0.22版本的驅動,且數據庫必須使用版本8,否則第二次啟動報錯 3、…

OpenAI文生視頻大模型Sora概述

Sora,美國人工智能研究公司OpenAI發布的人工智能文生視頻大模型(但OpenAI并未單純將其視為視頻模型,而是作為“世界模擬器” ),于2024年2月15日(美國當地時間)正式對外發布。 Sora可以根據用戶…

samber/lo 庫的使用方法:type

samber/lo 庫的使用方法:type samber/lo 是一個 Go 語言庫,提供了一些常用的集合操作函數,如 Filter、Map 和 FilterMap。 這個庫函數太多,因此我決定按照功能分別介紹,本文介紹的是 samber/lo 庫中type相關的函數。匯…

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

首先我們知道AOF和RDB都是Redis持久化的方法。RDB是Redis DB,一種二進制數據格式,這樣就是相當于全量保存數據快照了。AOF則是保存命令,然后恢復的時候重放命令。 AOF隨著時間推移,會越來越大,因為不斷往里追加命令。…

哪些行業適合做小程序?零售電商、餐飲娛樂、旅游酒店、教育生活、醫療保健、金融社交、體育健身、房產汽車、企管等,你的行業在其中么?

引言 在當今數字化時代,小程序成為了各行各業快速發展的數字工具之一。它的輕便、靈活的特性使得小程序在多個行業中找到了廣泛的應用。本文將探討哪些行業適合開發小程序,并介紹各行業中小程序的具體應用。 一、零售和電商 在當今數字化的商業環境中&…

C++ RAII

RAII定義 RAII(Resource Acquisition Is Initialization)是C編程中的一種重要的資源管理技術。它的核心思想是:資源的獲取應該在對象的構造階段進行,而資源的釋放則應該在對象的析構階段進行。通過利用C對象的生命周期和析構函數…

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 作為近年來最流行…