00 VFIO Neden Gerekli?
VFIO (Virtual Function I/O), donanım cihazlarını çekirdek sürücüleri olmaksızın — ve güvenli biçimde — kullanıcı alanı uygulamalarına ya da sanal makinelere doğrudan erişilebilir kılan bir Linux kernel alt sistemidir.
UIO'nun Sınırları
UIO (Userspace I/O), donanım kayıtlarını mmap() ile kullanıcı alanına açar; ancak ciddi güvenlik açıkları taşır. DMA işlemlerini denetleme mekanizması yoktur: kötü niyetli ya da hatalı bir UIO kullanıcısı, DMA motoruna çekirdek belleğine işaret eden adresler yazabilir ve tüm sistem belleğini okuyup değiştirebilir. Bu, özellikle sanallaştırma ortamlarında kabul edilemez bir tehdittir.
DMA Güvenliği Sorunu
Kullanıcı alanındaki bir süreç, bir PCIe cihazına DMA adresi gönderebilir. IOMMU olmadan bu adres doğrudan fiziksel bellek adresidir ve cihaz herhangi bir bellek bölgesine erişebilir. IOMMU, cihazın gördüğü IOVA (I/O Virtual Address) ile fiziksel adres arasına bir çeviri tablosu koyar; böylece cihaz yalnızca kendisine ayrılan belleği görebilir.
IOMMU Olmadan:
Kullanıcı Alani -> DMA adres yaz -> PCIe Cihaz -> RAM fiziksel adres
(TUM BELLEGE ERISIM)
IOMMU Ile (VFIO):
Kullanıcı Alani -> IOVA yaz -> PCIe Cihaz -> IOMMU -> Izinli RAM bölgesi
(SINIRLI ERISIM)
SR-IOV ve Geçiş Motivasyonu
SR-IOV (Single Root I/O Virtualization) özellikli bir NIC veya FPGA, tek bir fiziksel fonksiyonu (PF) onlarca sanal fonksiyona (VF) bölebilir. VFIO, her VF'yi ayrı bir sanal makineye güvenle geçirir: VM, donanıma doğrudan erişir ve hipervisör kopyalama yüküne maruz kalmaz. Gömülü Linuxta ise VFIO kullanıcı alanı sürücüleri (DPDK, SPDK) için temel altyapıdır.
VFIO'nun Sunduğu Güvenceler
VFIO, aşağıdaki güvenlik özelliklerini bir arada sunar:
- IOMMU korumalı DMA: Cihaz yalnızca haritalaması yapılmış belleğe erişebilir.
- Cihaz izolasyonu: Bir gruptaki cihazlar diğer grupları göremez.
- Kesme izolasyonu: Her kullanıcı alanı süreci yalnızca kendi cihazının kesmelerini alır.
- Hata kapsama: Cihaz hatası yalnızca ilgili kullanıcı sürecini etkiler; sistemi çöküşe götürmez.
01 VFIO Mimarisi
VFIO üç hiyerarşik nesne modeli üzerine kuruludur: konteyner (container), grup (group) ve cihaz (device). Bu modelin anlaşılması, VFIO kullanımının temelidir.
+--------------------------------------------------+ | VFIO Container | | (tek IOMMU adresi uzayi -- /dev/vfio/vfio) | | | | +---------------+ +---------------+ | | | VFIO Group | | VFIO Group | | | | /dev/vfio/0 | | /dev/vfio/1 | | | | | | | | | | +---------+ | | +---------+ | | | | | Cihaz | | | | Cihaz | | | | | | (PCI) | | | | (PCI) | | | | | +---------+ | | +---------+ | | | +---------------+ +---------------+ | +--------------------------------------------------+
Container (Konteyner)
Konteyner, /dev/vfio/vfio aygıt dosyasıyla temsil edilir. Bir IOMMU adresi uzayını kapsar. Kullanıcı alanı uygulaması bu dosyayı açarak IOMMU türünü sorgular ve DMA haritalamaları oluşturur. Birden fazla grup aynı konteynere bağlanabilir; bu durumda hepsi aynı IOMMU adresi uzayını paylaşır.
Group (Grup)
Grup, IOMMU'nun izolasyon birimini temsil eder. Aynı IOMMU grubundaki tüm cihazlar birbirini görür (ACS — Access Control Services yoksa). Güvenli geçiş için bir gruptaki tüm cihazların VFIO'ya bağlanmış olması gerekir. Gruplar /dev/vfio/N dosyaları olarak görünür.
Device (Cihaz)
Cihaz nesnesi, PCI konfigürasyon alanı okuma/yazma, MMIO bölgelerinin mmap'i ve kesme yapılandırması işlemlerini ioctl arayüzü ile sunar. Bir kullanıcı alanı sürücüsü, cihaz nesnesi aracılığıyla donanıma sanki çekirdek sürücüsüymüş gibi erişir.
IOMMU Domain Bağlaması
Bir grup konteynere bağlandığında, VFIO o gruba ait cihazları kernel'in IOMMU sürücüsü aracılığıyla konteyner IOMMU domain'ine atar. Artık DMA haritalamaları (VFIO_IOMMU_MAP_DMA ioctl) fiziksel bellekle IOVA arasında birebir eşlemeler oluşturur.
/* VFIO acilis sirasi */
int container = open("/dev/vfio/vfio", O_RDWR);
int group = open("/dev/vfio/42", O_RDWR);
/* Grubu konteynere bagla */
ioctl(group, VFIO_GROUP_SET_CONTAINER, &container);
/* IOMMU turunu etkinlestir */
ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU);
/* Cihaz taniticisi al */
int device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, "0000:03:00.0");
IOMMU Türleri
| IOMMU Türü | Açıklama | Platform |
|---|---|---|
| VFIO_TYPE1_IOMMU | Temel IOVA çeviri, pinned pages | x86 Intel VT-d / AMD-Vi |
| VFIO_TYPE1v2_IOMMU | MAP sırasında sayfa pinleme + dirty tracking | x86 gelişmiş |
| VFIO_ARM_SMMU_V3_IOMMU | ARM SMMUv3 doğrudan destek | ARM64 SoC |
| VFIO_NOIOMMU_IOMMU | IOMMU koruması YOK; yalnızca test için | Tüm platformlar |
02 Cihaz Bağlama
Bir PCIe cihazını VFIO'ya bağlamak için önce mevcut çekirdek sürücüsünü ayırmak (unbind), ardından cihazı vfio-pci sürücüsüne bağlamak (bind) gerekir.
Gerekli Çekirdek Modülleri
# IOMMU desteğini kernel komut satırında etkinlestir
# GRUB: intel_iommu=on veya amd_iommu=on
# /etc/default/grub dosyasinda:
# GRUB_CMDLINE_LINUX="intel_iommu=on iommu=pt"
# VFIO modullerini yukle
modprobe vfio
modprobe vfio_pci
modprobe vfio_iommu_type1
# Modullerın yuklendigini dogrula
lsmod | grep vfio
# vfio_pci 65536 0
# vfio_iommu_type1 36864 0
# vfio 36864 2 vfio_pci,vfio_iommu_type1
Cihazı Belirleme
# Hedef PCI cihazini bul (ornek: Ethernet kontroloru)
lspci -nn | grep -i ethernet
# 03:00.0 Ethernet controller [0200]: Intel XL710 [8086:1572]
# Cihazin IOMMU grubunu bul
readlink /sys/bus/pci/devices/0000:03:00.0/iommu_group
# ../../../kernel/iommu_groups/42
# Gruptaki tum cihazlari listele (hepsini VFIO'ya baglamak gerekebilir)
ls /sys/kernel/iommu_groups/42/devices/
# 0000:03:00.0
# 0000:03:00.1
Mevcut Sürücüyü Ayır (Unbind)
# Mevcut surucuyu bul
cat /sys/bus/pci/devices/0000:03:00.0/driver/module/drivers
# Suruculen ayir
echo "0000:03:00.0" > /sys/bus/pci/devices/0000:03:00.0/driver/unbind
# Cihazin surucusuz kaldığını dogrula
ls -l /sys/bus/pci/devices/0000:03:00.0/driver
# (baglantin yok)
vfio-pci'ye Bağla (Bind)
# Vendor/Device ID ogren
cat /sys/bus/pci/devices/0000:03:00.0/vendor # 0x8086
cat /sys/bus/pci/devices/0000:03:00.0/device # 0x1572
# Cihazi vfio-pci'ye yonlendir
echo "8086 1572" > /sys/bus/pci/drivers/vfio-pci/new_id
# Alternatif: dogrudan bind
echo "0000:03:00.0" > /sys/bus/pci/drivers/vfio-pci/bind
# Basariyi dogrula
ls /dev/vfio/
# vfio 42
Otomatik Bağlama: vfio-pci Modül Parametresi
# Sistem baslangicindan itibaren otomatik baglama icin
# /etc/modprobe.d/vfio.conf dosyasina ekle:
options vfio-pci ids=8086:1572,8086:1573
# GRUB'da da belirtilabilir:
# vfio-pci.ids=8086:1572,8086:1573
IOMMU Grup Durumunu Kontrol Et
# Tum cihazlarin gruba baglandigini dogrula
for dev in /sys/kernel/iommu_groups/42/devices/*; do
bdf=$(basename $dev)
driver=$(readlink $dev/driver | awk -F/ '{print $NF}')
echo "$bdf: $driver"
done
# 0000:03:00.0: vfio-pci -- dogru
# 0000:03:00.1: vfio-pci -- dogru (her ikisi de bağlı olmali)
03 Kullanıcı Alanından DMA
VFIO'nun en güçlü özelliği, kullanıcı alanından DMA haritalamaları oluşturabilmektir. IOMMU koruması altında, kullanıcı alanı belleği cihaza güvenle gösterilebilir.
DMA Belleği Tahsis Etme
DMA tamponu için kullanılacak bellek, sayfaların fiziksel olarak sabitlenmiş (pinned) ve ardışık (contiguous) veya IOMMU ile eşlenmiş olması gerekir. Tipik yaklaşım huge pages kullanmaktır; VFIO, sayfaları otomatik olarak pinler.
#include <linux/vfio.h>
#include <sys/mman.h>
/* 2 MB huge page tahsis et */
void *dma_buf = mmap(NULL, 2 * 1024 * 1024,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS |
MAP_HUGETLB | MAP_HUGE_2MB,
-1, 0);
if (dma_buf == MAP_FAILED) {
perror("mmap huge page");
exit(1);
}
VFIO_IOMMU_MAP_DMA ile IOVA Eşleme
Bellek tahsis edildikten sonra VFIO_IOMMU_MAP_DMA ioctl ile IOMMU'ya kaydettirilir. Cihaza verilecek adres IOVA'dır (fiziksel adres değil).
struct vfio_iommu_type1_dma_map dma_map = {
.argsz = sizeof(dma_map),
.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE,
.vaddr = (uint64_t)(uintptr_t)dma_buf, /* Sanal adres */
.iova = 0x10000000, /* Cihazin gorecegi IOVA */
.size = 2 * 1024 * 1024,
};
if (ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map)) {
perror("VFIO_IOMMU_MAP_DMA");
exit(1);
}
/* Artik cihaza 0x10000000 adresi verilebilir */
printf("DMA haritasi kuruldu: IOVA=0x%lx\n", dma_map.iova);
MMIO Bölgelerini mmap ile Açma
Cihazın BAR (Base Address Register) bölgeleri VFIO_DEVICE_GET_REGION_INFO ile sorgulanır, ardından mmap ile kullanıcı alanına açılır.
struct vfio_region_info reg = {
.argsz = sizeof(reg),
.index = VFIO_PCI_BAR0_REGION_INDEX,
};
ioctl(device, VFIO_DEVICE_GET_REGION_INFO, ®);
/* BAR0'i mmap et */
void *bar0 = mmap(NULL, reg.size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
device,
reg.offset);
if (bar0 == MAP_FAILED) {
perror("BAR0 mmap");
exit(1);
}
/* Dogrudan kayit erisimi */
volatile uint32_t *ctrl_reg = (uint32_t *)bar0;
*ctrl_reg = 0x1; /* DMA baslat */
/* Tamamlanma bitini bekle */
while (!(*ctrl_reg & 0x2))
;
DMA Eşlemesini Kaldırma
struct vfio_iommu_type1_dma_unmap dma_unmap = {
.argsz = sizeof(dma_unmap),
.flags = 0,
.iova = 0x10000000,
.size = 2 * 1024 * 1024,
};
ioctl(container, VFIO_IOMMU_UNMAP_DMA, &dma_unmap);
munmap(dma_buf, 2 * 1024 * 1024);
Çoklu DMA Bölgesi Yönetimi
Gerçek uygulamalarda birden fazla DMA bölgesi (RX halkası, TX halkası, kontrol yapıları) gerekir. Her biri için ayrı VFIO_IOMMU_MAP_DMA çağrısı yapılır ve IOVA uzayı çakışmayacak biçimde planlanır.
#define IOVA_RX_BASE 0x10000000
#define IOVA_TX_BASE 0x20000000
#define IOVA_CTRL_BASE 0x30000000
#define DMA_BUF_SIZE (2 * 1024 * 1024)
/* RX tamponu */
void *rx_buf = mmap_hugepage(DMA_BUF_SIZE);
vfio_map_dma(container, rx_buf, IOVA_RX_BASE, DMA_BUF_SIZE);
/* TX tamponu */
void *tx_buf = mmap_hugepage(DMA_BUF_SIZE);
vfio_map_dma(container, tx_buf, IOVA_TX_BASE, DMA_BUF_SIZE);
04 Kesme Yönetimi
VFIO, cihaz kesmelerini kullanıcı alanına eventfd mekanizması aracılığıyla iletir. Bu yaklaşım, kullanıcı alanı sürücüsünün busy-poll yapmak zorunda kalmadan kesmeleri verimli biçimde almasını sağlar.
Kesme Bilgisi Sorgulama
#include <linux/vfio.h>
struct vfio_irq_info irq_info = {
.argsz = sizeof(irq_info),
.index = VFIO_PCI_MSI_IRQ_INDEX,
};
if (ioctl(device, VFIO_DEVICE_GET_IRQ_INFO, &irq_info)) {
perror("VFIO_DEVICE_GET_IRQ_INFO");
exit(1);
}
printf("MSI kesmesi sayisi: %u\n", irq_info.count);
printf("Flags: 0x%x\n", irq_info.flags);
/* VFIO_IRQ_INFO_EVENTFD: eventfd destekli */
/* VFIO_IRQ_INFO_MASKABLE: maskelenebilir */
/* VFIO_IRQ_INFO_AUTOMASKED: otomatik maskeleme */
eventfd ile Kesme Yapılandırma
Her MSI/MSI-X vektörü için bir eventfd oluşturulur. VFIO_DEVICE_SET_IRQS ioctl bu eventfd'leri cihaz kesmelerine bağlar.
#include <sys/eventfd.h>
/* Kesme basina eventfd olustur */
int irq_fd = eventfd(0, 0);
if (irq_fd < 0) {
perror("eventfd");
exit(1);
}
/* VFIO_DEVICE_SET_IRQS yapisi dinamik boyutludur */
struct vfio_irq_set *irq_set;
int irq_set_size = sizeof(*irq_set) + sizeof(int);
irq_set = calloc(1, irq_set_size);
irq_set->argsz = irq_set_size;
irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD |
VFIO_IRQ_SET_ACTION_TRIGGER;
irq_set->index = VFIO_PCI_MSI_IRQ_INDEX;
irq_set->start = 0;
irq_set->count = 1;
/* eventfd dosya taniticisini yerlestiр */
memcpy(irq_set->data, &irq_fd, sizeof(irq_fd));
if (ioctl(device, VFIO_DEVICE_SET_IRQS, irq_set)) {
perror("VFIO_DEVICE_SET_IRQS");
exit(1);
}
free(irq_set);
Kesme Bekleme ve İşleme
#include <poll.h>
struct pollfd pfd = {
.fd = irq_fd,
.events = POLLIN,
};
while (1) {
int ret = poll(&pfd, 1, -1); /* Sonsuz bekleme */
if (ret < 0) {
perror("poll");
break;
}
if (pfd.revents & POLLIN) {
uint64_t count;
read(irq_fd, &count, sizeof(count));
printf("Kesme alindi! Sayac: %lu\n", count);
/* Donanim kesme isleyicisi... */
handle_interrupt();
}
}
IRQ Maskeleme ve Aç
İşlem süresince kesmeler maskelenebilir; bu, boyutlu işleme (batched processing) için gereklidir.
/* Maskeleme icin eventfd olustur */
int mask_fd = eventfd(0, 0);
irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD |
VFIO_IRQ_SET_ACTION_MASK;
memcpy(irq_set->data, &mask_fd, sizeof(mask_fd));
ioctl(device, VFIO_DEVICE_SET_IRQS, irq_set);
/* Kesmeyi maskele */
uint64_t val = 1;
write(mask_fd, &val, sizeof(val));
/* ... toplu isleme ... */
/* Kesmeyi ac (unmask) */
irq_set->flags = VFIO_IRQ_SET_DATA_NONE |
VFIO_IRQ_SET_ACTION_UNMASK;
ioctl(device, VFIO_DEVICE_SET_IRQS, irq_set);
| Kesme Türü | VFIO İndeksi | Açıklama |
|---|---|---|
| INTx (legacy) | VFIO_PCI_INTX_IRQ_INDEX | Eski PCIe kesmesi; paylaşımlı, yavaş |
| MSI | VFIO_PCI_MSI_IRQ_INDEX | Mesaj tabanlı kesme; genellikle 1 vektör |
| MSI-X | VFIO_PCI_MSIX_IRQ_INDEX | Çok vektörlü MSI; NIC queue başına kesme |
| Hata | VFIO_PCI_ERR_IRQ_INDEX | PCIe hata bildirimleri |
05 VFIO-platform
vfio-pci PCIe cihazlar içindir; gömülü sistemlerdeki platform cihazları (MMIO tabanlı, DT ile tanımlı IP blokları) için vfio-platform kullanılır. Bu, ARM SoC'larında FPGA blokları veya doğrudan adreslenebilir donanım hızlandırıcıları için kritik öneme sahiptir.
vfio-platform Mimarisi
vfio-platform, vfio_platform_device yapısı aracılığıyla platform cihazının MMIO bölgelerini ve kesmelerini, PCI'ya özgü olmayan bir arayüzle kullanıcı alanına sunar. SMMU (ARM'ın IOMMU'su) ile birlikte DMA koruması da sağlanır.
# Gerekli moduller
modprobe vfio
modprobe vfio_platform
modprobe vfio_iommu_type1
Platform Cihazını VFIO'ya Bağlama
# Platform cihazini bul
ls /sys/bus/platform/devices/ | grep fpga
# fpga@40000000
# Mevcut suruculen ayir
echo "fpga@40000000" > \
/sys/bus/platform/devices/fpga@40000000/driver/unbind
# vfio-platform'a bagla
echo "fpga@40000000" > \
/sys/bus/platform/drivers/vfio-platform/bind
# VFIO grubu olustu mu?
ls /dev/vfio/
Device Tree'de Reset Sağlayıcısı
Platform cihazları için VFIO, cihazın kullanıcı alanına geçmeden önce sıfırlanıp sıfırlanamayacağını kontrol eder. Cihazın bir reset mekanizması yoksa DT'ye özellik eklenmesi gerekebilir:
/* DT'de vfio-platform icin reset saglayicisi */
fpga: fpga@40000000 {
compatible = "vendor,my-fpga";
reg = <0x40000000 0x10000>;
interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;
resets = <&reset_controller 5>;
reset-names = "fpga-reset";
iommus = <&smmu 0x100>; /* SMMU stream ID */
};
Gömülü Kullanım Senaryosu
Gerçek zamanlı bir sinyal işleme uygulamasında FPGA hızlandırıcısının vfio-platform ile kullanıcı alanına geçirilmesi şu avantajları sağlar: çekirdek modülü yazma gerekmez, güncelleme için yeniden başlatma gerekmez ve süreç izolasyonu garanti edilir.
/* vfio-platform kullanici alani erisimi -- platform'a ozgu bolgeler */
struct vfio_region_info reg = {
.argsz = sizeof(reg),
.index = 0, /* Ilk MMIO bolgesi */
};
ioctl(device_fd, VFIO_DEVICE_GET_REGION_INFO, ®);
void *mmio = mmap(NULL, reg.size,
PROT_READ | PROT_WRITE,
MAP_SHARED, device_fd, reg.offset);
/* FPGA registerlerina dogrudan eris */
volatile uint32_t *fpga_ctrl = (uint32_t *)mmio;
fpga_ctrl[0] = 0x1; /* Islemi baslat */
/* Islem tamamlandi mi? */
while (!(fpga_ctrl[1] & 0x1))
; /* Durum bitini bekle */
vfio-platform ve vfio-pci Karşılaştırması
06 DPDK ve VFIO
DPDK (Data Plane Development Kit), yüksek hızlı ağ paketi işlemesi için kullanıcı alanı PMD (Poll Mode Driver) sürücüleri sunar. VFIO, DPDK'nın IOMMU korumalı DMA ortamında çalışması için tercih edilen altyapıdır.
DPDK VFIO Mimarisi
Kullanici Alani:
+------------------------------------------+
| DPDK Uygulamasi |
| +---------------+ +----------------+ |
| | rte_eal_init | | PMD Surucusu | |
| | (VFIO init) |->| (i40e, ixgbe) | |
| +---------------+ +----------------+ |
+---------------------+--------------------+
| VFIO ioctl
+---------------------v--------------------+
| Linux Kernel (VFIO + IOMMU) |
+------------------------------------------+
Huge Pages Yapılandırması
DPDK, DMA tamponları için büyük sayfa (huge page) kullanır. Bu sayfalar VFIO_IOMMU_MAP_DMA ile IOMMU'ya kaydettirilir.
# 1 GB huge page rezervasyonu (kernel komut satirinda)
# hugepagesz=1G hugepages=4 default_hugepagesz=1G
# Calisma aninda 2 MB huge page
echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
# Huge page mount noktasi
mkdir -p /dev/hugepages
mount -t hugetlbfs nodev /dev/hugepages
# Hugepage durumu kontrol et
cat /proc/meminfo | grep Huge
# HugePages_Total: 1024
# HugePages_Free: 1024
# Hugepagesize: 2048 kB
DPDK ile NIC Bağlama
# dpdk-devbind.py ile cihazi VFIO'ya bagla
dpdk-devbind.py --bind=vfio-pci 03:00.0
# Baglama durumunu kontrol et
dpdk-devbind.py --status-dev net
# DPDK uygulamasini VFIO ile baslat
./dpdk-testpmd \
-l 0-3 \
-n 4 \
--vfio-intr=msi \
-- \
--portmask=0x1 \
--nb-cores=2 \
--rxq=2 \
--txq=2 \
-i
Zero-Copy Paket İşleme
DPDK + VFIO kombinasyonu sayesinde ağ paketi NIC DMA tamponundan doğrudan uygulama belleğine kopyalanır; çekirdek arabelleği devreye girmez. Bu, 10 Gbps üzerindeki hızlarda bile düşük gecikme garantisi sağlar.
/* DPDK mbuf dogrudan NIC DMA bolgesinden geliyor */
struct rte_mbuf *pkt;
while (rte_eth_rx_burst(port_id, queue_id, &pkt, 1) == 1) {
/* Sifir kopya: veri NIC DMA tamponunda, mbuf pointer tutuyor */
uint8_t *data = rte_pktmbuf_mtod(pkt, uint8_t *);
uint16_t len = rte_pktmbuf_data_len(pkt);
/* Basit islem: UDP payload'i al */
process_packet(data, len);
rte_pktmbuf_free(pkt);
}
07 QEMU/KVM ile Cihaz Geçişi
VFIO'nun en yaygın kullanım senaryolarından biri, bir PCIe cihazı (GPU, NIC, FPGA) doğrudan bir KVM sanal makinesine geçirmektir. Sanal makine cihaza doğrudan erişir; hipervisör öykünme yükü sıfıra yaklaşır.
QEMU ile PCIe Geçişi
qemu-system-x86_64 \
-enable-kvm \
-m 8192 \
-cpu host \
-smp 4 \
\
-device vfio-pci,host=03:00.0,id=passthrough_nic \
\
-drive file=vm.qcow2,format=qcow2 \
\
-netdev user,id=mgmt \
-device virtio-net,netdev=mgmt \
\
-nographic
Çoklu Cihaz Geçişi (SR-IOV VF)
SR-IOV destekli bir NIC'ten sanal fonksiyonlar (VF) oluşturarak her VM'e ayrı VF geçirilebilir. Bu, ağ yoğun iş yüklerinde fiziksel NIC'i birden fazla VM arasında doğrudan paylaştırır.
# PF uzerinde VF olustur (i40e surucusu)
echo 4 > /sys/bus/pci/devices/0000:03:00.0/sriov_numvfs
# Olusturulan VF'leri listele
lspci | grep "Virtual Function"
# 03:02.0 Ethernet controller: Intel XL710 VF
# 03:02.1 Ethernet controller: Intel XL710 VF
# Her VF'yi ayri VFIO grubuna bagla
dpdk-devbind.py --bind=vfio-pci 03:02.0
dpdk-devbind.py --bind=vfio-pci 03:02.1
# VM1'e VF gecir
qemu-system-x86_64 -enable-kvm \
-device vfio-pci,host=03:02.0 \
-m 4096 vm1.qcow2 &
# VM2'ye VF gecir
qemu-system-x86_64 -enable-kvm \
-device vfio-pci,host=03:02.1 \
-m 4096 vm2.qcow2 &
Huge Page ve IOMMU Zorunlulukları
# KVM + VFIO icin GRUB parametreleri (/etc/default/grub)
# GRUB_CMDLINE_LINUX="intel_iommu=on iommu=pt
# hugepagesz=1G hugepages=16
# default_hugepagesz=1G"
# QEMU'ya huge page bellek tahsisi
qemu-system-x86_64 \
-m 8G \
-mem-path /dev/hugepages \
-mem-prealloc \
-enable-kvm \
-cpu host \
-device vfio-pci,host=03:00.0
PCIe ACS (Access Control Services)
IOMMU izolasyonu için PCIe cihazlar arası ACS etkinleştirilmelidir. Aksi takdirde aynı PCIe köprüsü altındaki cihazlar birbirinin DMA trafiğini görebilir ve aynı IOMMU grubuna düşer.
# ACS durumunu kontrol et
lspci -vvv | grep -A2 "Access Control"
# ACS override (test ortami -- uretimde dikkatli kullan)
# Kernel parametresi: pcie_acs_override=downstream,multifunction
GPU Geçişi Örneği
# Nvidia GPU'yu VFIO'ya bagla
echo "10de:2204" > /sys/bus/pci/drivers/vfio-pci/new_id
# QEMU ile GPU passthrough
qemu-system-x86_64 \
-enable-kvm \
-m 16G \
-cpu host,hv_relaxed,hv_spinlocks=0x1fff,hv_vapic,hv_time \
-device vfio-pci,host=01:00.0,multifunction=on,x-vga=on \
-device vfio-pci,host=01:00.1 \
-vga none \
-nographic \
win11.qcow2
08 Hata Ayıklama
VFIO sorunlarının büyük çoğunluğu IOMMU yapılandırması, grup izolasyonu veya sürücü çakışmalarından kaynaklanır. Sistematik bir hata ayıklama süreci bu sorunları hızla tespit eder.
IOMMU ve VFIO Durumu Kontrolü
# IOMMU etkin mi?
dmesg | grep -i iommu | head -20
# [ 0.123456] DMAR: IOMMU enabled
# [ 0.234567] iommu: Default domain type: Translated
# VFIO grup durumu
ls -la /dev/vfio/
# crw------- 1 root root 246, 0 ... /dev/vfio/vfio
# crw------- 1 root root 246, 1 ... /dev/vfio/42
# Grup sahipligini kullaniciya ver
chown emirpehlevan /dev/vfio/42
dmesg IOMMU Hata Mesajları
# IOMMU fault loglarini izle
dmesg -w | grep -E "iommu|DMAR|vfio"
# Yaygin IOMMU hata mesajlari:
# DMAR: DRHD: handling fault status reg 3
# DMAR: [DMA Read] Request device [03:00.0] fault addr 0xdeadbeef
# Bunlar genellikle IOVA eslenenmis adrese DMA denemesini gosterir
# IOMMU domain bilgisi (debugfs)
cat /sys/kernel/debug/iommu/devices
VFIO Grup Durumu Sorgulama
#include <linux/vfio.h>
/* Grup durumunu sorgula */
struct vfio_group_status status = {
.argsz = sizeof(status),
};
int group_fd = open("/dev/vfio/42", O_RDWR);
ioctl(group_fd, VFIO_GROUP_GET_STATUS, &status);
if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) {
fprintf(stderr, "Grup uygun degil: "
"grupdaki tum cihazlar vfio-pci'ye baglanmali\n");
}
if (!(status.flags & VFIO_GROUP_FLAGS_CONTAINER_SET)) {
fprintf(stderr, "Grup henuz konteynere baglanmamis\n");
}
lspci ile Cihaz Sürücü Kontrolü
# Cihaz suruculunu dogrula
lspci -k -s 03:00.0
# 03:00.0 Ethernet controller: Intel XL710
# Subsystem: Intel Ethernet CNA X710
# Kernel driver in use: vfio-pci
# Kernel modules: i40e
# Tum VFIO cihazlarini listele
for bdf in /sys/bus/pci/drivers/vfio-pci/*/; do
echo $(basename $bdf)
done
IOMMU Bilgi Sorgulama
/* IOMMU bilgisini ioctl ile al */
struct vfio_iommu_type1_info iommu_info = {
.argsz = sizeof(iommu_info),
};
ioctl(container, VFIO_IOMMU_GET_INFO, &iommu_info);
printf("IOMMU flags: 0x%x\n", iommu_info.flags);
printf("IOMMU PGSIZE: 0x%lx\n", iommu_info.iova_pgsizes);
/* Desteklenen sayfa boyutlari: 0x1000 (4KB), 0x200000 (2MB) */
| Sorun | Olası Neden | Çözüm |
|---|---|---|
| VFIO_GROUP_FLAGS_VIABLE değil | Gruptaki bir cihaz hâlâ kernel sürücüsüne bağlı | Tüm grup cihazlarını unbind + vfio-pci bind |
| VFIO_IOMMU_MAP_DMA EINVAL | IOVA hizalaması veya aralık sorunu | IOMMU sayfa boyutuna hizala (4KB/2MB/1GB) |
| IOMMU DMA fault | Eşlenmemiş IOVA'ya DMA denemesi | DMA adres hesabını ve MAP_DMA çağrısını kontrol et |
| lspci sürücü "vfio-pci" değil | bind komutu başarısız | dmesg'de hata oku; cihaz ID doğrula |
| Kesme alınamıyor | MSI desteklenmiyor ya da grup konteynere bağlanmamış | VFIO_PCI_INTX_IRQ_INDEX ile başla; group container set kontrol et |