【原創】Linux虛擬化KVM-Qemu分析(十)之virtio驅動
阿新 • • 發佈:2021-02-25
# 背景
- `Read the fucking source code!` --By 魯迅
- `A picture is worth a thousand words.` --By 高爾基
說明:
1. KVM版本:5.9.1
2. QEMU版本:5.0.0
3. 工具:Source Insight 3.5, Visio
4. 文章同步在部落格園:`https://www.cnblogs.com/LoyenWang/`
# 1. 概述
![](https://img2020.cnblogs.com/blog/1771657/202102/1771657-20210224230011372-860838932.png)
- 前篇文章講完了Qemu中如何來建立Virtio Device,本文將圍繞Guest OS中的Virtio Driver來展開;
看一下Guest OS(Linux)中的Virtio框架高層架構圖:
![](https://img2020.cnblogs.com/blog/1771657/202102/1771657-20210224230018412-1701114651.png)
- 核心模組為virtio和virtqueue,其他高層的驅動都是基於核心模組之上構建的;
- 顯然,本文會延續這個系列,繼續分析virtio-net驅動,重心在整體流程和框架上,細節不表;
- virtio-net,又是一個virtio裝置,又是一個PCI裝置,那麼驅動會怎麼組織呢?帶著問題上路吧。
# 2. 資料結構
說到驅動怎麼能不提linux裝置驅動模型呢,感興趣的朋友可以去看看PCI系列分析文章,簡單來說就是核心建立匯流排用於掛載裝置,匯流排負責裝置與驅動的匹配。Linux核心建立了一個virtio bus:
![](https://img2020.cnblogs.com/blog/1771657/202102/1771657-20210224230026389-1415523459.png)
- virtio裝置和virtio驅動,通過`virtio_device_id`來匹配,而這個都是在virtio規範中定義好的;
- `virtio_device`結構中有一個`struct virtio_config_ops`,函式集由驅動來進行指定,用於操作具體的裝置;
本文描述的virtio-net驅動,既是一個virtio裝置,也是一個pci裝置,在核心中通過結構體`struct virtio_pci_device`來組織:
![](https://img2020.cnblogs.com/blog/1771657/202102/1771657-20210224230035773-340142374.png)
- 該結構體中維護了幾個IO區域:`Common, ISR, Device, Notify`,用於獲取virtio裝置的各種資訊,這個也是由virtio規範決定的;
- 通常來說一個virtio裝置,由以下幾個部分組成:
1. Device status field
2. Feature bits
3. Notifications
4. Device Configuration space
5. One or more virtqueues
- 從結構體看,它用於充當pci裝置和virtio裝置的紐帶,後續也會在probe函式中針對不同的部分進行對應的初始化;
以匯流排的匹配視角來看就是這樣子的:
![](https://img2020.cnblogs.com/blog/1771657/202102/1771657-20210224230048521-644906865.png)
# 3. 流程分析
## 3.1 virtio匯流排建立
先看一下virtio匯流排的建立,virtio bus當然也算是基建了:
![](https://img2020.cnblogs.com/blog/1771657/202102/1771657-20210224230056465-1815162121.png)
- `bus_register`註冊virtio匯流排,匯流排負責匹配,在匹配成功後呼叫通用的`virtio_dev_probe`函式;
- 千里姻緣一線牽,當Virtio的ID號能對上時,就會觸發驅動探測,所以什麼時候進行設備註冊呢?
## 3.2 virtio驅動呼叫流程
詳細的細節,建議閱讀之前PCI驅動系列的分析文章,下邊羅列關鍵部分:
![](https://img2020.cnblogs.com/blog/1771657/202102/1771657-20210224230103153-2040554903.png)
- virtio-net裝置通過掛在pci總線上,系統在PCI子系統初始化時會去列舉所有的裝置,並將列舉的設備註冊進系統;
- 系統在匹配上之後,呼叫裝置的驅動;
![](https://img2020.cnblogs.com/blog/1771657/202102/1771657-20210224230400403-1000958582.png)
- PCI裝置根據Vendor ID來匹配驅動;
- virtio規範中規定基於PCI的virtio裝置,Vendor ID號為:`0x1AF4`,因此最終呼叫的驅動入口為`virtio_pci_probe`;
![](https://img2020.cnblogs.com/blog/1771657/202102/1771657-20210224230417971-437424297.png)
- 在probe函式中分配`struct virtio_pci_device`結構,前文中也提到過它負責將virtio裝置和pci裝置繫結到一起,最終會在兩個裝置驅動的probe函式中完成整體結構的初始化,也就是`virtio_pci_probe`完成一部分,實際的virtio裝置驅動中完成一部分;
- `virtio_pci_modern_probe`:該函式的內容就與virtio規範緊密相關了,簡單來說,virtio裝置都會按照規範填充common、device、isr、notification等功能部分,而`virtio_pci_modern_probe`函式通過`virtio_pci_find_capability`去獲取對應的能力,並且通過`map_capability`完成IO空間的對映;
- `virtio_pci_probe`中還設定了`virtio_pci_config_ops`操作函式集,並傳遞給virtio驅動,在驅動中呼叫這些回撥函式來操作virtio裝置;
- `register_virtio_device`:向系統註冊virtio裝置,從而也就觸發了virtio匯流排的匹配操作,最終呼叫`virtio_dev_probe`函式;
- `virtio_dev_probe`函式中按照virtio規範分階段設定不同的狀態、獲取virtio裝置的feature等,並最終呼叫實際裝置的驅動程式了;
At last,終於摸到本文要說的virtio-net的驅動的入口了,當然,文章也要戛然而止了。
整體執行流程及框架應該清楚了,細節就留給大家了,待續。。。
# 參考
`https://developer.ibm.com/technologies/linux/articles/l-virtio/`
`Virtual I/O Device (VIRTIO) Version 1.1`
歡迎關注個人公眾號,不定期更新技術文章。
![](https://img2020.cnblogs.com/blog/1771657/202102/1771657-20210224230213154-2752558