1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
|
\documentclass[a4paper,10pt]{article}
\usepackage[outer=2cm,inner=2cm]{geometry}
\usepackage[hidelinks]{hyperref}
\usepackage{framed}
\usepackage{lmodern}
\usepackage{fontspec}
\usepackage{ragged2e}
\setmainfont{Latin Modern Mono}
\title{PCIe GPU passthrough on Debian}
\author{Nao Pross}
\begin{document}\tt
\maketitle
\section{Introduzione}
Recentemente grazie all'evoluzione delle tecnologie di virtualizzazione \`e
diventato possibile passare direttamente un dispositivo collegato in uno
slot PCI ad una macchina virtuale, che può quindi usufruire dell'hardware a
performance quasi nativa. Un computer configurato per funzionare in tale
maniera apparirà come se fossero due computer distinti con due monitors (o
ad uno con un kvm switch). Il vantaggio di un setup del genere è la
possibilità di cambiare immediatamente da un sistema operativo all’altro
senza dover attendere che ma macchina si riavvii (in un dualboot) e si
evitano potenziali problemi come Windows Update che rendono il PC inusabile.
\section{Limitazioni}
Come ogni sistema si ha delle limitazioni quali
\begin{itemize}
\item Per poter vedere l'uscita video della VM si deve collegare un
display ad una delle uscite della scheda grafica passata
\item Non \`e possibile passare la iGPU ad una VM (\`e possibile ma con
altre tecnologie ancora sperimentali)
\item Non \`e possibile passare una GPU discreta di un laptop (es NVIDIA
Optimus) poich\`e non \`e possibile visualizzare il video in uscita
\end{itemize}
\section{Funzionamento (In grandi linee)}
Le macchine virtuali vengono emulate grazie ad un software chiamato QEMU che
supporta il passaggio di hardware PCI alla VM, inoltre QEMU utilizza un
estensione chiamata KVM che permette di creare delle macchine virtuali
collegate alla kernel dunque ottimizzate. Altrimenti QEMU dovrebbe emulare
l’intero processore su cui viene virtualizzato il sistema operativo. Infine
per gestire questi due componenti LibVirt è un software che salva le
configurazioni di QEMU in dei file XML e che permette alle VM di essere
tenute accese in background. \\
Passando una scheda video in PCI ad una macchina virtuale permette di
utilizzare la potenza grafica hardware che sarebbe altrimenti emulata (le
grafichce virtuali di QEMU non sono per niente ottimizzate, per tali scopi è
meglio utilizzare VirtualBox o VMWare Fusion). Quindi per poter vedere la
grafica della macchina virtuale si deve collegare un monitor ad una delle
entrate della scheda video.
\section{Hardware Requirements}
\begin{itemize}
\item Intel processor with iGPU (or 2 GPUs)
\item Processor with VT-x and VT-d
\item NVIDIA or AMD graphics card (2 if with no iGPU)
\item Motherboard with VT-d support
\item Motherboard with UEFI based bootloader
\item Motherboard with IOMMU support (per configurazioni avanzate)
\end{itemize}
\section{Software Requirements}
\begin{itemize}
\item Linux Kernel with iommu\_groups support ( kernel >= 3.9 )
\end{itemize}
\section{Hardware utilizzato}
Per questo questo test ho usato una configurazione hardware esagerata, anche
con un sistema decisamente meno costoso \`e possibile realizzare il
progetto.
\begin{itemize}
\item ASUS Z170-A motherboard
\item Intel Core i7 Skylake 6700 CPU with Intel Graphics 530
\item MSI NVIDIA GTX1050Ti OC 4GB
\item 32GB DDR4
\end{itemize}
\section{BIOS Settings}
Per incominciare è necessario controllare che nel BIOS siano attivate le
tecnologie di virtualizzazione VT-x e VT-d. Inoltre se si vuole utilizzare
la iGPU in alcune motherboard (come nel mio caso) si deve modificare un
impostazione nel bios per non disattivarla qundo viene inserita una scheda
grafica secondaria.
\section{Sistema Operativo}
Per avere un sistema stabile consiglio di utilizzare Debian Linux poiché
essendo utilizzato soprattuto nei servers il ciclo di testing e updates è
molto lento in modo da avere sempre un supporto legacy e un sistema stabile.
Purtroppo però attualmente (12.2016) Debian 8 Jessie ha ancora la kernel
3.16 quindi si deve utilizzare la versione testing (9) Stretch. Anche se
Debian Testing non \`e pensato per essere utilizzato come sistema primario
(i bug possono essere ignorati a lungo nei repo) il supporto di
aggiornamenti software \`e garantito per un perido molto pi\`u esteso delle
altre distro. \\
Prima di installare il sistema operativo è consigliato rimuovere la GPU che
si utilizzerà per il passthrough dallo slot PCI per evitare che Linux si
autoconfiguri per utilizzare i suoi driver grafici. Una volta installato il
sistema si devono installare i seguenti pacchetti:
\begin{framed}\raggedright
\% sudo apt-get install \textbackslash \\
~ qemu-kvm libvirt-bin virtinst bridge-utils virt-manager ssh-askpass ovmf
\end{framed}
\section{Kernel Modules and GRUB}
Normalmente Debian viene con GRUB2 preinstallato come bootloader, se si
dovesse avere un altro bootloader si deve semplicemente passare gli stessi
parametri alla kernel quando si avvia.
\subsection{IOMMU}
Dalla kernel 3.9 Linux ha introdotto gli iommu\_groups che permettono di
mappare dispositivi di memoria reale ad indirizzi virtuali che possono
essere a loro volta passati alla VM. Per poter utilizzare questa
funzionalit\`a la si deve abilitare inoltre \`e necessario anche il supporto
degli ACS per poter suddividere i sottocomponenti del gruppo iommu (per
esempio le porte individuali di un estensione di USB via PCI). Per attivare
le funzionalit\`a si deve passare alla kernel i seguenti parametri:
\begin{framed}\raggedright \footnotesize
\# file: /etc/default/grub \\
... \\
GRUB\_DEFAULT\_CMDLINE\_LINUX\_DEFAULT="intel\_iommu=on iommu=1 pcie\_acs\_override=downstream" \\
...
\end{framed}
Per aggiornale le impostazioni installate
\begin{framed}\raggedright
\% sudo update-grub
\end{framed}
\section{GPU Settings}
Per evitare che Debian utilizzi la GPU all’avvio si deve modificare le
impostazioni dell’initramfs in maniera tale che il modulo della kernel non
venga avviato. Per sapere il nome del modulo della kernel caricato per la
GPU si può usare il seguente comando (nel mio caso la scheda è una NVIDIA):
\begin{framed}\raggedright \scriptsize
\% lspci -nnk | grep -i -A2 nvidia \\
02:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP107 [GeForce GTX 1050 Ti] [10de:1c82] (rev a1) \\
~~~~~~~~Subsystem: Micro-Star International Co., Ltd. [MSI] GP107 [GeForce GTX 1050 Ti] [1462:8c96] \\
~~~~~~~~Kernel driver in use: {\bf nouveau} \\
-- \\
02:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:0fb9] (rev a1) \\
~~~~~~~~Subsystem: Micro-Star International Co., Ltd. [MSI] Device [1462:8c96] \\
~~~~~~~~Kernel driver in use: snd\_hda\_intel
\end{framed}
Nel file di configurazione
\begin{framed}\raggedright
\# file: /etc/modprobe.d/blacklist.conf \\
... \\
blacklist nouveau
\end{framed}
Infine per aggiornare la configurazione
\begin{framed}\raggedright
\% sudo update-initramfs -u
\end{framed}
\section{Driver binding}
Per passare un dispositivo ad una VM si deve indicare alla kernel di
utilizzare il modulo `vfio-pci', perci\`o \`e necessario inizializzare
questi dispositivi all'avvio. Per questo possiamo usare questo script preso
da \href{https://www.reddit.com/r/pcmasterrace/comments/3lno0t/gpu_passthrough_revisited_an_updated_guide_on_how/?ref=share&ref_source=link}{qui}
salvandolo come `/usr/local/bin/vfio-bind'.
\begin{framed}\raggedright
\# file: /usr/local/bin/vfio-bind
\begin{verbatim}
#!/bin/bash
modprobe vfio-pci
for dev in "$@"; do
vendor=$(cat /sys/bus/pci/devices/$dev/vendor)
device=$(cat /sys/bus/pci/devices/$dev/device)
if [ -e /sys/bus/pci/devices/$dev/driver ]; then
echo $dev > /sys/bus/pci/devices/$dev/driver/unbind
fi
echo $vendor $device > /sys/bus/pci/drivers/vfio-pci/new_id
done \end{verbatim}
\end{framed}
Attivando la flag per poterlo eseguire:
\begin{framed}\raggedright
\% sudo chmod +x /usr/local/bin/vfio-bind
\end{framed}
Con questo script possiamo collegare il driver per la virtualizzazione a
qualsiasi dispositivo PCI nella seguente maniera:
\begin{framed}\raggedright
\% sudo vfio-bind <indirizzi pci>
\end{framed}
Per trovare l'indizirro dei dispositivi che vogliamo passare utilizziamo di
nuovo il seguente comando:
\begin{framed}\raggedright \scriptsize
\% lspci -nnk | grep -i -A2 nvidia \\
02:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP107 [GeForce GTX 1050 Ti] [10de:1c82] (rev a1) \\
~~~~~~~~Subsystem: Micro-Star International Co., Ltd. [MSI] GP107 [GeForce GTX 1050 Ti] [1462:8c96] \\
~~~~~~~~Kernel driver in use: nouveau \\
-- \\
02:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:0fb9] (rev a1) \\
~~~~~~~~Subsystem: Micro-Star International Co., Ltd. [MSI] Device [1462:8c96] \\
~~~~~~~~Kernel driver in use: snd\_hda\_intel
\end{framed}
Nel mio caso gli indirizzi da passare alla VM sono `0000:02:00.0' per il
video e `0000:02:00.1' per l'audio. Successivamente possiamo automatizzare
la configurazione in modo che il driver per la virtualizzazione venga
avviato subito all'avvio della macchina. Quindi lo facciamo con una unit di
SystemD.
\begin{framed}\raggedright
\# file: /etc/systemd/system/vfio-pci-bind.service
\begin{verbatim}
[Unit]
Description=Bind PCI devices to the virtio driver
[Service]
Type=oneshot
ExecStart=/usr/local/bin/vfio-bind 0000:02:00.0 0000:02:00.1
[Install]
WantedBy=multi-user.target
Before=libvirt-guests.service \end{verbatim}
\end{framed}
\section{Installare la VM}
Come sistema operativo guest conglio Windows 8+, poich\`e il supporto
dell'UEFI \`e migliorato e per esperienza trovo che Windows 7 sia pi\`u
complicato da configurare (errori di drivers video).
\subsection{Dispositivo di installazione}
Per installare l'OS guest \`e necessario avere un immagine di installazione
bootable perch\`e OVMF non supporta l'avvio di CDROM (o di altri sistemi
legacy). Da un disco di installazione normalmente \`e possibile genereare un
disco di installazione uefi utilizzando una memoria USB formattata in FAT32.
\subsection{Installazione}
Per creare la VM in libvirt \`e possibile utilizzare virt-manager che ha uno
strumento con interfaccia grafica, in alternativa \`e possibile utilizzare
virt-install dalla linea di comando.
\begin{framed}
\begin{verbatim}
% virt-install --connect qemu:///system \
--name win8 \
--memory 8169 \
--vcpus sockets=1,cores=2,threads=2 \
--cpu Skylake-Client \
--metadata title="Windows 8.1" \
--disk ~/vm_images/windows8.raw,cache=none \
--network ... \end{verbatim}
\end{framed}
\end{document}
|