# PCI通透 [TOC] ## 簡介 為了架設基於[Proxmox VE](https://pve.proxmox.com/wiki/Main_Page)的伺服器,並讓[Proxmox VE](https://pve.proxmox.com/wiki/Main_Page)所建立的VM能夠存取伺服器的顯示卡資源,我們必須要啟用 **PCI通透(PCI Passthrough)** 的功能來將PCI的通訊傳遞至VM之中。 ## 前置準備 在開始設定`IOMMU`之前,我們必須先完成幾項前置作業: 1. 於BIOS啟動CPU虛擬化功能。[^1] 2. 於BIOS開啟記憶體虛擬化以及相關設置。[^2] 3. 確認PVE是使用`GRUB`還是`systemd-boot`作為開機引導。[^3] ## 流程圖 首先,我們透過下方的流程圖來了解設定PCI通透並將GPU分配給VM該如何進行。整個流程主要分成四大部分 1. 開機引導設置 2. IOMMU重映射設定 3. 模組設定 4. 虛擬機配置設定 ![](https://i.imgur.com/ITruQwd.png) ## 詳細流程解說 ### 開機引導設定 根據PVE所使用的開機引導不同,需要進行的設置也有所不同。開機引導主要有兩種:`GRUB`以及`systemd-boot`。 --- #### GRUB 針對`GRUB`的設定,需要修改`/etc/default/grub`中的`GRUB_CMDLINE_LINUX_DEFAULT`參數。根據CPU供應商的不同,設置上也有些微的差異。 ##### AMD CPU 針對AMD CPU,需要在`GRUB_CMDLINE_LINUX_DEFAULT`加入`amd_iommu=on`的參數。 ```= #修改前 GRUB_CMDLINE_LINUX_DEFAULT='quiet' #修改後 GRUB_CMDLINE_LINUX_DEFAULT='quiet amd_iommu=on' ``` ##### Intel CPU 針對Intel CPU,需要在`GRUB_CMDLINE_LINUX_DEFAULT`加入`intel_iommu=on`的參數。 ```= #修改前 GRUB_CMDLINE_LINUX_DEFAULT='quiet' #修改後 GRUB_CMDLINE_LINUX_DEFAULT='quiet intel_iommu=on' ``` --- #### systemd-boot 針對`systemd-boot`的設定,需要在`/etc/kernel/cmdline`的第一行增加額外的參數。根據CPU供應商的不同,設置上也有些微的差異。 ##### AMD CPU 針對AMD CPU,需要在第一行後方加入`amd_iommu=on`的參數。 ```= #修改前 root=ZFS=rpool/ROOT/pve-1 boot=zfs #修改後 root=ZFS=rpool/ROOT/pve-1 boot=zfs amd_iommu=on ``` ##### Intel CPU 針對Intel CPU,需要在第一行後方加入`intel_iommu=on`的參數。 ```= #修改前 root=ZFS=rpool/ROOT/pve-1 boot=zfs #修改後 root=ZFS=rpool/ROOT/pve-1 boot=zfs intel_iommu=on ``` --- #### 更新設定 在完成上方修改之後,需要執行以下指令來更新設定: ```bash= proxmox-boot-tool refresh ``` 這些設定將在重新啟動後被套用。 ### IOMMU重映射設定 對於使用PCI通透來說,設置中斷重映射(Interrupt Remapping)是必要的,若沒有設定這個機制,將無法正確的使用PCI通透的相關功能,並且可能出現以下錯誤訊息: ``` Failed to assign device "[device name]": Operation not permitted' or 'Interrupt Remapping hardware not found, passing devices to unprivileged domains is insecure. ``` 大部分的狀況下,無論是AMD或是Intel的CPU都有支援中斷重映射的機制。我們可以透過`dmesg | grep 'remapping'`來檢查。若有支援,將會跳出以下訊息: * AMD CPU: "AMD-Vi: Interrupt remapping enabled" * Intel CPU: "DMAR-IR: Enabled IRQ remapping in x2apic mode" ('x2apic' can be different on old CPUs, but should still work) 若檢查後並沒有跳出以上訊息,則需要手動設置。設置方法為將`options vfio_iommu_type1 allow_unsafe_interrupts=1`參數加入到`/etc/modprobe.d/iommu_unsafe_interrupts.conf`之中。 ```bash= echo "options vfio_iommu_type1 allow_unsafe_interrupts=1" > /etc/modprobe.d/iommu_unsafe_interrupts.conf ``` 此改動的目的是在CPU平台未提供中斷重映射的機制時,允許使用不安全的中斷方式來進行重映射的中斷。 ### 模組設定 #### 啟用VFIO模組 為了讓硬體設備可以被虛擬機直接取用,還需要額外更改`/etc/modules`的設定來啟用`VFIO`模組。只要在`/etc/modules`中加入以下四行文字即可。 ```= vfio vfio_iommu_type1 vfio_pci vfio_virqfd ``` #### 將驅動程式加入黑名單避免PVE系統取用顯示卡資源 為了避免PVE系統使用顯示卡資源並排擠到虛擬機的資源,我們需要編輯`/etc/modprobe.d/blacklist.conf`來使主系統的部分驅動程式無法取用顯示卡資源。 ```bash= echo "blacklist nouveau" >> /etc/modprobe.d/blacklist.conf echo "blacklist nvidia" >> /etc/modprobe.d/blacklist.conf ``` :::warning 上方列為黑名單的是Nvidia以及與Nvidia顯示卡有關的驅動程式。對於Intel以及AMD顯示卡的部分,可以參考此篇[文章](https://wiki.freedomstu.com/books/proxmox-ve-%E8%99%9B%E6%93%AC%E7%B3%BB%E7%B5%B1%E8%A8%98%E9%8C%84/page/%E5%9C%A8-vm-%E8%A3%A1%E9%9D%A2%E4%BD%BF%E7%94%A8%E9%A1%AF%E7%A4%BA%E5%8D%A1)中的設定。 ::: #### 將顯示卡資訊寫入於VFIO設定中 為了讓VFIO模組了解哪一個PCI設備是顯示卡,我們需要以下步驟: 1. 找出顯示卡對應ID 為了找出顯示卡對應的ID,我們可以使用以下指令: ```bash= lspci | grep VGA ``` 以我自己的工作站而言,會有以下結果: ``` 01:00.0 VGA compatible controller: NVIDIA Corporation GA102 [GeForce RTX 3090] (rev a1) ``` 這時,我可以得知我的顯示卡ID為`01:00.0`。 2. 獲取顯示卡規格 在取得了顯示卡ID之後,我們便可以利用以下指令取得顯示卡的細部規格: ```bash= lspci -n -s 01:00 ``` 以我的工作站來說,結果如以下所示: ```= root@pve:~# lspci -n -s 01:00 01:00.0 0300: 10de:2204 (rev a1) 01:00.1 0403: 10de:1aef (rev a1) ``` 上方可以看現兩個結果。其中`10de:2204`就是我們在上一步驟中所找到的顯示卡;而`10de:1aef`則是顯示卡所附帶的音效卡。在PCI通透中我們只需要設定顯示卡的部分即可。 3. 將顯示卡ID以及相關設定寫入`/etc/modprobe.d/vfio.conf`檔案中 在上一步中,我們取得了顯示卡ID對應的規格以及編號,接下來我們只需要將上一步取得的`10de:2204`搭配必要的參數寫入`vfio.conf`檔案中即可。以下是用以將設定寫入的指令: ```bash= echo "options vfio-pci ids=10de:2204" > /etc/modprobe.d/vfio.conf ``` 如果想要關閉VGA功能,可以在後方加入`disable_vga=1`參數: ```bash= echo "options vfio-pci ids=10de:2204 disable_vga=1" > /etc/modprobe.d/vfio.conf ``` 在寫入完成後,執行下方指令並重新開機即可套用前述的所有設定。 ```bash= update-initramfs -u ``` :::warning 在此我們只有示範了最基礎的設定,對於更完整以及更多的設定選擇,可以參考[PVE官網](https://pve.proxmox.com/wiki/Pci_passthrough#Required_Modules)所提供的介紹。 ::: ## 虛擬機配置設定 在前幾個部分中,我們已經完成了PCI通透大部分所需要的系統設置。接下來,我們將進行對虛擬機的設定,讓虛擬機可以順利的取用顯示卡資源。 1. 創建並設定虛擬機的配置 首先,我們先利用網頁中的`Create VM`按鈕來建立一個新的虛擬機。 ![](https://i.imgur.com/dYnVLSQ.png) 接下來,將BIOS設定為`OVMF(UEFI)`模式,並將`Machine`選項設定為q35。 ![](https://i.imgur.com/CyIYWoL.png) 在硬碟設定上,可以使用`IDE`或`VirtIO Block`(但需要額外設定VirtIO driver)。 ![](https://i.imgur.com/c1SMZvT.png) 在CPU的設定上,核心數量可以自訂,但是必須將`Type`設置為`host`以避免出錯。[^4] ![](https://i.imgur.com/sPyuqK9.png) 完成上述的必要設定之後,只要儲存設定即可完成初步設定。 :::warning 完成此步驟後,請先啟動虛擬機,並完成基礎的系統安裝。後續將顯示卡指定至虛擬機時,可能因為`disable_vga=1`的設定導致PVE預設使用的`noVNC`無法連線至虛擬機,進而導致無法進行系統安裝。 ::: 2. 指定PCI裝置至虛擬機 在虛擬機建立後,我們可以到`Harware`中點選`Add`來新增`PCI device`。 ![](https://i.imgur.com/1EhC1To.png) 然後在選單中選取對應的顯示卡。 ![](https://i.imgur.com/B9kN0UA.png) 將`All Functions`、`Primary GPU`以及`PCI Express`選項打勾。 ![](https://i.imgur.com/hPvjVDJ.png) 接下來只要點選`Add`並啟動虛擬機即可。 ## 參考資料及相關資料 1. PVE官網對於PCI通透的[設定手冊](https://pve.proxmox.com/wiki/Pci_passthrough#Required_Modules)。 2. PVE官網對於Host Bootloader的[相關手冊](https://pve.proxmox.com/wiki/Host_Bootloader)。 3. IOMMU技術[介紹](https://zhuanlan.zhihu.com/p/348826888)。 4. Intel虛擬化技術[介紹](https://www.intel.com.tw/content/www/tw/zh/virtualization/virtualization-technology/intel-virtualization-technology.html)。 5. [在 VM 裡面使用顯示卡 ](https://wiki.freedomstu.com/books/proxmox-ve-%E8%99%9B%E6%93%AC%E7%B3%BB%E7%B5%B1%E8%A8%98%E9%8C%84/page/%E5%9C%A8-vm-%E8%A3%A1%E9%9D%A2%E4%BD%BF%E7%94%A8%E9%A1%AF%E7%A4%BA%E5%8D%A1) 6. VFIO[介紹](https://www.kernel.org/doc/html/latest/driver-api/vfio.html) 7. OSSLab的PVE設定[教學文章](https://www.osslab.com.tw/proxmox-ve-pci-e-pass/) [^1]: 於兩大x86平台中分別被稱為Intel VT-X以及AMD SVM。啟用方式可以參考此份[介紹](https://bce.berkeley.edu/enabling-virtualization-in-your-pc-bios.html)。 [^2]: 記憶體虛擬化係指Intel VT-d以及AMD IOMMU技術,其設定依主機板供應商不同而有所差異。AMD在ASRock主機板平台上的設定可以參考此份[介紹](https://www.reddit.com/r/VFIO/comments/cm6xme/comment/ew0r5x7/?utm_source=share&utm_medium=web2x&context=3)。 [^3]: 關於如何辨別,可以使用`proxmox-boot-tool status`指令進行查看。詳細可以參考此篇[介紹](https://pve.proxmox.com/wiki/Host_Bootloader)。 [^4]: 以Tensorflow為例,預設的KVM選項會讓Tensorflow編譯出現錯誤`The TensorFlow library was compiled to use SSE4.2 instructions, but these aren't available on your machine. Aborted (core dumped)`。