浪潮信息工程師:帶你瞭解設備透傳虛擬機的快速啓動技術優化方案

編者按:將物理設備通過 vfio 透傳給虛擬機是虛擬化常用的技術,但當爲虛擬機分配比較大的內存時,虛擬機的啓動時間會明顯變慢,可能由十幾秒延長至數分鐘,嚴重影響用戶使用體驗。本文整理自龍蜥大講堂 51 期,浪潮信息操作系統研發工程師參與技術分享,介紹了設備透傳虛擬機啓動慢的原因及優化方法,以下爲此次分享內容:

技術背景:大內存虛擬機設備透傳啓動延遲

虛擬機設備透傳時存在問題,比如將網卡或者 GPU 這些 pci 設備通過宿主機的 vfio 透傳到虛擬機,並且又爲虛擬機分配了比較大的內存時,虛擬機的啓動時間會明顯變慢。特別是分配了幾百 G,甚至上 TB 的內存時,就會有比較明顯的啓動延遲,可能由十幾秒延遲到數分鐘。由於只有第一次啓動時,也就是 qemu 進程啓動時纔會影響,所以一般來說用戶可以接受這個啓動延遲,但如果用戶有頻繁創建銷燬虛擬機的需求時就會有比較差的使用體驗。

把設備透傳到虛擬機內部之後將會使用虛擬機內部的驅動程序對設備進行操作,這裏有一個重要的問題是在虛擬機內部該如何進行 DMA 操作,因爲 DMA 是不會經過 CPU 的,所以使用的是物理地址,但虛擬機內部看到的物理地址實際上只是宿主機上 qemu 進程申請的虛擬地址,直接用來做 DMA 肯定是不行的。這裏就需要藉助 IOMMU 來實現,虛擬機內 DMA 訪問的 GPA,經過 iommu 映射到宿主機上的物理地址 HPA2,另外虛擬機內的驅動程序也可以經過 MMU/EPT,把 GVA 映射爲 HPA1,最終需要 HPA1 和 HPA2 這兩個物理地址是同一個才能讓設備正常運行。(如下圖)

爲了實現 HPA1 等於 HPA2,需要 GPA 到 HPA 的映射是固定的並進行 iommu 表的建立,vfio 是通過 VFIO_IOMMU_MAP_DMA 命令實現。

後面使用到了 free page reporting 機制,這裏介紹下 free page reporting 機制的原理,首先該機制提供了回調函數的註冊,每隔 2 秒會遍歷每個 zone 中的空閒頁並調用回調函數進行處理,但只處理 buddy 中高階內存頁,就是 pageblock_order 階及以上的內存,在 X86 架構下就是處理 9 階和 10 階的內存頁,也就是 2M 和 4M 的內存頁,這樣也是保證能夠處理 2M 的透明大頁。

另一方面在處理時會保證每個 zone 的水線要比最低水線高 64M,因爲如果 zone 的水線本身已經夠低了,還從 zone 中申請內存,可能就會觸發內存回收,這就會影響一些程序的性能。

通內存預清零加速 qemu 進程的內存初始化過程

下圖是一個虛擬機啓動時的火焰圖,這裏的虛擬機啓動是指 qemu 進程開始執行到虛擬機內部出現 BIOS 界面爲止,不包括虛擬機內部的內核加載、服務運行過程,從火焰圖可以看到最耗時的函數是 clear_subpage 函數,根據函數調用關係可以看到是從 qemu 端調用了 ioctl 系統調用,這裏就是要將虛擬機內存大小的虛擬內存執行 iommu 映射工作,將虛擬地址映射到指定的物理地址上。這其中就需要先將虛擬地址執行 page fault,並將虛擬機地址和物理地址的關係固定下來,再執行 iommu 映射操作。

應用程序執行 page fault 時最終都會執行 clear_user_highpage 對內存進行清零操作,這個函數相對來說耗時較長,是虛擬機啓動時間慢的重要原因,當然我們可以選擇不進行內存清零,但這會把宿主機上的一些敏感信息傳遞給虛擬機,造成安全影響,所以一般我們需要對內存進行清零。

爲了解決內存頁清零耗時比較長的問題,我們可以基於 free page reporting 機制實現內存頁的預清零,過程比較簡單,這裏簡單說下原理。首先通過 free page reporting 接口註冊自己的 hook 函數,然後這個 hook 函數會週期性的被調用,每次調用會將 buddy 中的 2M/4M 空閒頁執行清零操作,並在 page 的 flag 上設置清零 flag,最後在應用程序申請的內存觸發缺頁異常並需要對內存進行清零時,會首先判斷該頁是否已經設置了清零 flag,如果已設置則跳過耗時較長的清零操作。還有一點就是內存頁的清零屬於耗時但不緊急的操作,如果當前 CPU 有其它的事情要做,則優先執行別的工作,只有在 CPU 空閒時才執行內存清零。

通過大塊內存 pin 降低 qemu 的內存 pin 時間

下圖是優化了內存頁預清零之後的火焰圖,可以看到 clear_user_page 函數的執行時間已經非常短了,總體執行時間比較長的幾個函數是 follow_page_mask、handle_mm_fault、find_extern_vma,下面就從代碼角度來看哪個函數還可以進行優化。

vfio_pin_map_dma(…)
{
    while(size)
    {
    npage = vfio_pin_pages_remote(dma,start_vaddr,size,&pfn,limit)
    vfio_iommu_map(iommu,start_vaddr,pfn,npage,true)
    }將物理地址連續的內存執行iommu map
}

IOMMU_MAP_DMA 的 ioctl 進入內核空間會執行到 vfio_pin_map_dma 函數,在這個函數裏會對虛擬機內存大小的虛擬內存進行 iommu map 操作,首先調用 vfio_pin_pages_remote 進行內存 pin 操作,這個函數的返回值 npage 是物理地址連續的N個內存頁,然後調用 vfio_iommu_map 執行 iommu map 操作,比如對於 500G 內存的虛擬機來說會執行很多次這兩個函數。

vfio_pin_pages_remote(…)
{
    for (…)
    {    
    pin_user_pages_remote(NULL, mm, vaddr, 1, flags | FOLL_LONGTERM, page, NULL, NULL);
    pfn = page_to_pfn(page[0]);
    if(pfn != *pfn_base + pinned)
    break;
    }每次只pin一個page,然後判斷和上一個page是否物理連續
}

vfio_pin_pages_remote 這個函數會返回物理連續的多個內存頁,爲了實現這功能,每次調用 pin_user_pages_remote 獲取一個頁,然後判斷和上一個頁是否物理連續。

get_user_pages(…)
{
  while(npages)
  {
    if(!vma ||  start >= vma->vm_end)//相鄰的兩個虛擬內存頁是否屬於同一個vma
    {
      vma = find_extern_vma(…)
    }
    page = follow_page_mask(vma,start,…);
    if(!page)
      faultin_page(…)
  }
}

前面說的 pin_user_pages_remote 函數執行了很多次,這個函數裏主要是調用了 get_user_pages,根據需要 pin 的 page 個數,首先判斷相鄰的兩個虛擬內存頁是否屬於同一個 vma,如果不是則調用 find_extern_vma 函數獲取 vma,這個函數從火焰圖來看也是耗時比較長的,下面就是執行 follow_page_mask 獲取頁表,如果還未分配物理頁則調用 page fault。

後面的兩個函數執行是無法避免的,現在就看 find_extern_vma 函數,因爲虛擬機的物理內存是 qemu mmap 的連續虛擬內存,屬於同一個 vma,所以每次如果 pin 多個 page 可以跳過頻繁執行的 find_extern_vma。

下面看一下內核社區的優化方案,5.12 內核合入主線,通過修改 vfio 代碼實現,commit 說明有 8% 的性能提升。

優化原理是在 pin_user_pages_remote 函數裏每次獲取 512 個 page,當然這些page可能是不連續的,然後將這 512 個 page 中連續的部分識別出來分別執行iommu,因爲我們系統中是開啓透明大頁的,所以一般來說這 512 個 page 都是物理連續的,只需要執行一次 iommu 就可以。

測試結果

測試環境爲虛擬機分配了 512G 的內存,做了網卡的設備透傳,虛擬機的啓動時間計算方法爲從 qemu 進程啓動開始到出現 grub 界面爲止,還有就是內存預清零在最理想的情況下,即當前系統上所有的 free page 已經清零完畢。

最終的測試結果:默認情況下(即開啓透明大頁),如果只開啓大塊內存 pin,啓動時間從 80 秒降低至 60 秒,如果再開啓內存頁預清零,啓動時間將降低至 12 秒。再看下虛擬機內存使用大頁內存的情況下的測試結果,在使用 2M 或 1G 大頁時開啓大塊內存 pin 功能時,虛擬機的啓動時間基本從 36 秒降低至 18 秒,就算在開啓內存頁預清零,啓動時間也沒有變化,因爲內存預清零不能作用於標準大頁。

  • 虛擬機分配 512G 內存。
  • 時間計算方法:time virsh start testvm,時間爲從 qemu 啓動到出現 grub 界面爲止。
  • 內存預清零在最理想情況下,即當前系統上所有 free page 已經清零完畢。

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章