1. 程式人生 > 其它 >virtio簡介(五)—— virtio_blk裝置分析

virtio簡介(五)—— virtio_blk裝置分析

一: 建立過程關鍵函式

1. virtblk_probe

  虛擬機器在啟動過程中,virtio bus上檢測到有virtio塊裝置,就呼叫probe函式來插入這個virtio block裝置(前端建立的virtio裝置都是PCI裝置,因此,在對應的virtio裝置的probe函式呼叫之前,都會呼叫virtio-pci裝置的probe函式,在系統中先插入一個virtio-pci裝置)。

  初始化裝置的散列表,從簡介(一)的流程圖我們知道,系統的 IO請求會先對映到散列表中。

  

  virtio_find_single_vq為virtio塊裝置生成一個vring_virtqueue。

  

  這個函式通過呼叫在virtio-pci中為virtio device定義的OPS,find_vqs就跳轉到vp_find_vqs,之後呼叫vp_try_to_find_vqs。

  

   

  在函式vp_try_to_find_vqs中,setup_vq為virtio裝置建立vring_virtqueue佇列。

2. vring_virtqueue

  每個virtio裝置都有一個virtqueue介面,它提供了一些對vring進行操作的函式,如add_buf,get_buf,kick等,而vring_virtqueue是virtqueue及vring的管理結構。我們在virtio裝置中儲存virtqueue指標,當要使用它操作vring時,通過to_vvq來獲得其管理結構vring_virtqueue。

  Vring_virtqueue的資料結構如下所示:

   

  Vring,是IO請求地址的真正存放空間(virtio block裝置在初始化的時候,會申請了兩個頁大小的空間)。num_free,表示vring_desc表中還有多少項是空閒可用的;free_head,表示在vring_desc中第一個空閒表項的位置(並且系統會將其餘空閒表項通過vring_desc的next串聯成一個空閒連結串列);num_added,表示我們在通知對端進行讀寫的時候,與上次通知相比,我們添加了多少個新的IO請求到vring_desc中;last_used_idx,表示vring_used表中的idx上次IO操作之後被更新到哪個位置(與當前的vring_used->idx相減即可獲得本次QEMU處理了多少個vring_desc表中的資料)。

3. setup_vq

  

  通過to_vp_device將virtio_device轉換成virtio-pci,我們在前端虛擬機器內建立的virtio裝置都是一個pci裝置,因此可以利用PCI裝置的配置空間來完成前後端訊息通知,vp_dev->ioaddr就指向配置空間的暫存器集合的首地址。

  

  iowrite寫暫存器VIRTIO_PCI_QUEUE_SEL來通知QEMU端,當前初始化的是第index號vring_virtqueue;ioread則從QEMU端讀取vring_desc表,共有多少項(virtio block裝置設定為128項)。

  

  根據之前ioread獲得的表項數來確定vring共享區域的大小,並呼叫alloc_pages_exact在虛擬機器裡為vring_virtqueue分配記憶體空間。

   

  virt_to_phys(info->queue) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT將虛擬機器的虛擬機器地址轉換成實體地址,偏移VIRTIO_PCI_QUEUE_ADDR_SHIFT(12位)得到頁號。

  iowrite將vring_virtqueue在虛擬機器內的物理頁號寫到暫存器VIRTIO_PCI_QUEUE_PFN,產生一個kvm_exit,QEMU端會捕獲這個exit,並根據暫存器的地址將這個物理頁號賦值給QEMU端維護的virtqueue。

  

4. Vring_new_virtqueue 

   

  引數pages,是在setup_vq中給vring_virtqueue申請的實體記憶體頁地址;引數num,是在setup_vq中通過ioread獲得的vring_desc表的表項數目;vring_align,為4096,表示一個頁的大小;notify,是virtio-pci註冊的函式vp_notify,當要通知qemu端取vring中的資料時,就呼叫notify函式;callback,是qemu端完成IO請求返回後,前端處理的回撥函式,virtio-blk的回撥函式就是blk_done。

二: QEMU獲取VRING地址

  在1.3節中,提到了virtio_map函式註冊了對PCI配置空間暫存器的監聽函式,當虛擬機器產生kvm_exit時,會根據exit的原因將退出資料分發,IO請求會被髮送到這些監聽函式,他們會呼叫virtio_ioport_write/read確定前端讀/寫了哪個暫存器,觸發何種動作。

virtio_ioport_write對應前端的iowrite操作,virtio_ioport_read對應前端的ioread操作。

1. virtio_ioport_write

  virtio_ioport_write函式根據我們iowrite的地址來區分寫了哪個暫存器,要執行之後的哪些操作。

  對於vring這個資料區域的共享,前端虛擬機器在分配物理頁之後,呼叫

  

  來通知後端QEMU程序

  因此,我們可以看到在函式中:

  從配置空間對應的位置獲得前端寫入的物理頁號(客戶機的物理頁)。

2. virtio_queue_set_addr

  該函式將QEMU程序維護的virtio裝置的virtqueue地址初始化,使得前端和後端指向同一片地址空間。(由於KVM下虛擬機器是一個QEMU程序,因此虛擬機器的記憶體是由QEMU程序來分配的,並且在QEMU程序內有mem_slot連結串列進行維護, QEMU程序知道了虛擬機器建立的VRING的GPA就可以通過簡單的轉換在自己的HVA地址中找到VRING的記憶體地址)。

3. virtqueue_init

  

  將QEMU程序內的vring中的三個表的地址初始化。

三:完整的讀寫流程