天天看點

【原創】Linux虛拟化KVM-Qemu分析(九)之virtio裝置

<code>Read the fucking source code!</code> --By 魯迅

<code>A picture is worth a thousand words.</code> --By 高爾基

說明:

KVM版本:5.9.1

QEMU版本:5.0.0

工具:Source Insight 3.5, Visio

文章同步在部落格園:<code>https://www.cnblogs.com/LoyenWang/</code>

新的一年, 大家牛起來!

祝小姐姐們:

落雁沉魚 蘭質蕙心 明眸皓齒 螓首蛾眉 天生麗質 天香國色 杏臉桃腮 煦色韶光 涎玉沫珠 宜嗔宜喜 遠山芙蓉 豔色絕世 餘霞成绮 阿嬌金屋 逞嬌呈美 國色天香 花顔月貌 絕色佳人 暗香盈袖 閉月羞花 傾國傾城 溫婉娴淑 千嬌百媚 儀态萬千...

祝男的:

新年好。

先來張圖:

【原創】Linux虛拟化KVM-Qemu分析(九)之virtio裝置

圖中羅列了四個關鍵子產品:<code>Virtio Device</code>、<code>Virtio Driver</code>、<code>Virtqueue</code>、<code>Notification(eventfd/irqfd)</code>;

<code>Virtio Driver</code>:前端部分,處理使用者請求,并将I/O請求轉移到後端;

<code>Virtio Device</code>:後端部分,由Qemu來實作,接收前端的I/O請求,并通過實體裝置進行I/O操作;

<code>Virtqueue</code>:中間層部分,用于資料的傳輸;

<code>Notification</code>:互動方式,用于異步事件的通知;

想在一篇文章中寫完這四個子產品,有點<code>too yong too simple</code>,是以,看起來又是一個系列文章了。

本文先從Qemu側的virtio device入手,我會選擇從一個實際的裝置來闡述,沒錯,還是上篇文章中提到的網絡裝置。

在Qemu的網卡虛拟化時,通常會建立一個虛拟網卡前端和虛拟網卡後端,如下圖:

【原創】Linux虛拟化KVM-Qemu分析(九)之virtio裝置

在虛拟機建立的時候指定參數:<code>-netdev tap, id = tap0, -device virtio-net-pci, netdev=tap0</code>;

建立一個<code>Tap</code>網卡後端裝置;

建立一個<code>Virtio-Net</code>網卡前端裝置;

網卡前端裝置和後端裝置進行互動,最終與Host的驅動完成資料的收發;

全文圍繞着<code>Tap</code>裝置的建立和<code>Virtio-Net</code>裝置的建立展開。

入口流程如下:

【原創】Linux虛拟化KVM-Qemu分析(九)之virtio裝置

Qemu的代碼閱讀起來還是比較費勁的,各種盤根錯節,裡邊充斥着面向對象的思想,先給自己挖個坑,後續會專題研究的,<code>this is for you, you have my words.</code>;

圖中與本文相關的有三個子產品:1)子產品初始化;2)網絡裝置初始化;3)裝置初始化;

Qemu中裝置模拟通過<code>type_init</code>先編譯進系統,在<code>module_call_init</code>時進行回調,比如圖中的<code>xxx_register_types</code>,在這些函數中都是根據<code>TypeInfo</code>類型資訊來建立具體的實作資訊;

<code>net_init_client</code>用來建立網絡裝置,比如<code>Tap</code>裝置;

<code>device_init_func</code>根據Qemu指令的傳入參數建立虛拟裝置,比如<code>Virtio-Net</code>;

下邊進入細節,<code>the devil is in the details</code>。

從上文中,我們知道,<code>Tap</code>與<code>Virtio-Net</code>屬于前後端的關系,最終是通過結構體分别指向對方,如下圖:

【原創】Linux虛拟化KVM-Qemu分析(九)之virtio裝置

<code>NetClientState</code>是網卡模拟的核心結構,表示網絡裝置中的幾個端點,兩個端點通過<code>peer</code>指向對方;

建立Tap裝置的主要工作就是建立一個<code>NetClientState</code>結構,并添加到<code>net_clients</code>連結清單中:

【原創】Linux虛拟化KVM-Qemu分析(九)之virtio裝置

函數的調用細節如下圖:

【原創】Linux虛拟化KVM-Qemu分析(九)之virtio裝置

處理流程隻關注了核心的處理流程,整個過程有很多關于傳入參數的處理,選擇性忽略了;

<code>net_tap_init</code>:與Host的<code>tun</code>驅動進行互動,其實質就是打開該裝置檔案,并進行相應的配置等;

<code>net_tap_fd_init</code>:根據<code>net_tap_info</code>結構,建立<code>NetClientState</code>,并進行相關設定,這裡邊<code>net_tap_info</code>結構體中的接收函數指針用于實際的資料傳輸處理;

<code>tap_read_poll</code>用于将fd添加到Qemu的AioContext中,用于異步響應,當有資料來臨時,捕獲事件并進行處理;

以上就是Tap後端的建立過程,下文将針對前端建立了。

這是一個複雜的流程。

Qemu中用C語言實作了面向對象的模型,用于對裝置進行抽象,精妙!

針對Virtio-Net裝置,結構體及拓撲組織關系如下圖:

【原創】Linux虛拟化KVM-Qemu分析(九)之virtio裝置

<code>DeviceState</code>作為所有裝置的父類,其中派生了<code>VirtIODevice</code>和<code>PCIDevice</code>,而本文研究的<code>Virtio-Net</code>派生自<code>VirtIODevice</code>;

Qemu中會虛拟一個PCI總線,同時建立<code>virtio-net-pci</code>,<code>virtio-balloon-pci</code>,<code>virtio-scsi-pci</code>等PCI代理裝置,這些代理裝置挂載在PCI總線上,同時會建立Virtio總線,用于挂載最終的裝置,比如<code>VirtIONet</code>;

PCI代理裝置就是一個紐帶;

與裝置建立相關的三個函數,可以從<code>device_init_func</code>入口跟蹤得知:

【原創】Linux虛拟化KVM-Qemu分析(九)之virtio裝置

當Qemu指令通過<code>-device</code>傳入參數時,<code>device_init_func</code>會根據參數去查找裝置,并最終調用到該裝置對應的類初始化函數、對象初始化函數、以及realize函數;

是以,我們的分析就是這三個入口;

【原創】Linux虛拟化KVM-Qemu分析(九)之virtio裝置

在網卡虛拟化過程中,參數隻需要指定PCI代理裝置即可,也就是<code>-device virtio-net-pci, netdev=tap0</code>,進而會調用到<code>virtio_net_pci_class_init</code>函數;

由于實作了類的繼承關系,在子類初始化之前,需要先調用父類的實作,圖中也表明了繼承關系以及調用函數順序;

C語言實作繼承,也就是将父對象放置在自己結構體的開始位置,圖中的顔色能看出來;

類初始化結束後,開始對象的建立:

【原創】Linux虛拟化KVM-Qemu分析(九)之virtio裝置

針對<code>Virtio-Net-PCI</code>的執行個體化比較簡單,作為代理,負責将它的後繼對象初始化,也就是本文的前端裝置<code>Virtio-Net</code>;

【原創】Linux虛拟化KVM-Qemu分析(九)之virtio裝置

<code>realize</code>的調用,比較繞,簡單來說,它的類繼承關系中存在多個<code>realize</code>的函數指針,最終會從父類開始執行,一直調用到子類,而這些函數指針的初始化在什麼時候做的呢?沒錯,就是在class_init類初始化的時候,進行了指派,細節不表,結論可靠;

最終的調用關系就如圖了;

到目前為止,我們似乎都還沒有看到<code>Virtio-Net</code>裝置的相關操作,不用着急,已經很接近真相了:

【原創】Linux虛拟化KVM-Qemu分析(九)之virtio裝置

<code>virtio_net_pci_realize</code>函數,會觸發<code>virtio_device_realize</code>的調用,該函數是一個通用的virtio裝置實作函數,所有的virtio裝置都會調用,而我們的前端裝置<code>Virtio-Net</code>也是virtio裝置;

<code>virtio_net_device_realize</code>就到了我們的主角了,它進行了virtio通用的設定(後續在資料通信中再分析),還建立了一個<code>NetClientState</code>端點,與<code>Tap</code>裝置對應,分别指向了對方,惺惺相惜,各自安好;

<code>virtio_bus_device_plugged</code>表示裝置插入總線時的處理,完成的工作就是按照PCI總線規劃,配置各類資訊,以便與Guest OS中的virtio驅動互動,後續的文章再分析了;

本文基本捋清了虛拟網卡前端裝置和後端裝置的建立過程,完成的工作隻是綁定了彼此,資料互動以及通知機制,留給後續吧。

<code>《 Virtual I/O Device (VIRTIO) Version 1.1》</code>

<code>https://www.redhat.com/en/blog/virtio-devices-and-drivers-overview-headjack-and-phone</code>

歡迎關注個人公衆号,不定期更新技術文章。

【原創】Linux虛拟化KVM-Qemu分析(九)之virtio裝置

作者:LoyenWang

出處:https://www.cnblogs.com/LoyenWang/

公衆号:<b>LoyenWang</b>

版權:本文版權歸作者和部落格園共有

轉載:歡迎轉載,但未經作者同意,必須保留此段聲明;必須在文章中給出原文連接配接;否則必究法律責任

繼續閱讀