Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Virtio: Paravirtualized I/O for Virtual Machines

Virtio is the standard paravirtualization framework for I/O in virtual machines. Instead of emulating real hardware, virtio defines an efficient guest-hypervisor communication protocol that both sides understand natively. This chapter covers virtio device types (net, blk, scsi), vhost, vDPA, packed virtqueues, and performance tuning.


1. What Is Paravirtualization?

flowchart TB
    subgraph Emulated["Full Emulation (Slow)"]
        E_DRV["Guest Driver<br>(e.g., e1000)"] --> E_EM["QEMU Emulator<br>(full hardware model)"]
    end
    subgraph PV["Paravirtualization (Fast)"]
        P_DRV["Virtio Driver<br>(guest)"] --> P_VQ["Virtqueue<br>(shared ring)"] --> P_HYP["Hypervisor Backend"]
    end
    subgraph Direct["Passthrough (Fastest)"]
        D_DRV["Native Driver"] --> D_HW["Physical Hardware"]
    end
ApproachPerformanceCompatibilityComplexity
Full emulation10–30% nativeWorks with any OSHigh (emulate hardware)
Paravirt (virtio)70–95% nativeRequires virtio driverMedium
Passthrough (VFIO)~100% nativeNeeds dedicated HWHigh

2. Virtqueue Architecture

2.1 Split Virtqueues (Traditional)

A virtqueue consists of three descriptor tables shared between guest and host:

flowchart LR
    subgraph Guest
        AVAIL["Available Ring<br>(guest writes)"]
    end
    subgraph Shared_Memory["Shared Memory"]
        DESC["Descriptor Table<br>(buffer pointers)"]
        USED["Used Ring<br>(host writes)"]
    end
    AVAIL -->|"buffer index"| DESC
    DESC -->|"completed buffers"| USED
    USED -->|"notification"| Guest
  • Descriptor Table — Array of buffer descriptors (addr, len, flags, next)
  • Available Ring — Guest tells host which descriptors are ready
  • Used Ring — Host tells guest which descriptors are done

2.2 Packed Virtqueues (Linux 4.20+, Virtio 1.1)

Packed virtqueues merge all three tables into a single descriptor ring, improving cache locality and reducing memory barriers.

flowchart LR
    subgraph Packed["Packed Virtqueue"]
        D0["Desc 0<br>avail+used"]
        D1["Desc 1<br>avail+used"]
        D2["Desc 2<br>avail+used"]
        D3["Desc 3<br>avail+used"]
    end
    D0 --> D1 --> D2 --> D3 --> D0

Benefits:

  • Better cache performance — single contiguous memory region
  • Fewer memory barriers — driver and device flags in same descriptor
  • Up to 10–20% throughput improvement for small messages

3. Virtio Device Types

3.1 virtio-net — Network Device

flowchart LR
    subgraph Guest
        APP[Application] --> VNET[virtio-net driver]
    end
    subgraph Host
        BACKEND["TAP device / vhost-net"]
        BRIDGE["Linux Bridge / OVS"]
    end
    VNET -->|"virtqueue TX/RX"| BACKEND
    BACKEND --> BRIDGE

Guest driver:

# Linux guest — check driver
ethtool -i eth0
# driver: virtio_net

# Features negotiation
ethtool -k eth0
# tcp-segmentation-offload: on
# generic-receive-offload: on

Host configuration (QEMU):

qemu-system-x86_64 \
    -device virtio-net-pci,netdev=net0,mac=52:54:00:12:34:56 \
    -netdev tap,id=net0,script=/etc/qemu-ifup,vhost=on

libvirt XML:

<interface type='bridge'>
  <source bridge='br0'/>
  <model type='virtio'/>
  <driver name='vhost' queues='4'>
    <host mrg_rxbuf='on'/>
  </driver>
</interface>

3.2 virtio-blk — Block Device

Simple block device interface. One virtqueue per device.

qemu-system-x86_64 \
    -device virtio-blk-pci,drive=drive0 \
    -drive file=disk.qcow2,format=qcow2,if=none,id=drive0,aio=native,cache=none

Guest verification:

# Check virtio-blk driver
lsblk -t
# NAME   ALIGNMENT MIN-IO OPT-IO PHY-SEC LOG-SEC ROTA SCHED    RQ-SIZE
# vda           0    512      0     512      512    0 mq-deadline     256

cat /sys/block/vda/queue/scheduler
# [mq-deadline] kyber none

3.3 virtio-scsi — SCSI Device

More feature-rich than virtio-blk: supports multiple LUNs, SCSI commands, rescan, and discard/TRIM.

qemu-system-x86_64 \
    -device virtio-scsi-pci,id=scsi0 \
    -device scsi-hd,bus=scsi0.0,drive=drive0 \
    -drive file=disk.qcow2,format=qcow2,if=none,id=drive0

Comparison: virtio-blk vs virtio-scsi

Featurevirtio-blkvirtio-scsi
Multiple LUNsOne per deviceMany per controller
SCSI commandsNoYes (pass-through)
Discard/TRIMLimitedFull
Multi-queueYesYes
PerformanceSlightly faster (simple)Slightly slower (more features)
Use caseSimple diskComplex storage, multi-disk

4. vhost — Kernel-Based Backend

4.1 What Is vhost?

vhost moves the virtio backend from QEMU userspace into the kernel, reducing context switches and data copies.

flowchart LR
    subgraph Without["Without vhost"]
        QEMU1["QEMU process"] --> TAP1["TAP device"]
    end
    subgraph With["With vhost-net"]
        VHOST["vhost-net kernel thread"] --> TAP2["TAP device"]
    end
    Guest1 -->|virtqueue| QEMU1
    Guest2 -->|virtqueue| VHOST

4.2 vhost-net

# Enable vhost-net in QEMU
qemu-system-x86_64 \
    -device virtio-net-pci,netdev=net0 \
    -netdev tap,id=net0,vhost=on

# Verify vhost is active
lsmod | grep vhost
# vhost_net              32768  1
# vhost                  53248  1 vhost_net

# Check vhost thread
ps aux | grep vhost
# root  1234  0.5  vhost-1234-qemu

4.3 vhost-scsi

# Use vhost-scsi for SCSI target
qemu-system-x86_64 \
    -device vhost-scsi-pci,wwpn=naa.5001405888888888 \
    -object memory-backend-file,id=mem,size=4G,mem-path=/dev/hugepages,share=on \
    -numa node,memdev=mem

4.4 vhost-user

For userspace datapath (DPDK, OVS-DPDK):

# Start OVS-DPDK with vhost-user
ovs-vsctl add-port br0 vhost-user0 -- \
    set Interface vhost-user0 type=dpdkvhostuserclient \
    options:vhost-server-path=/var/run/openvswitch/vhost-user0

5. vDPA — Virtio Data Path Acceleration

5.1 What Is vDPA?

vDPA provides a hardware-accelerated virtio datapath. Hardware vendors implement the virtio datapath in their NIC firmware while the control plane remains software-defined.

flowchart TB
    subgraph Guest
        DRV[virtio-net driver]
    end
    subgraph vDPA
        VDPA[vDPA bus]
        subgraph HW["Hardware"]
            CTRL["Control plane<br>(software)"]
            DATA["Data plane<br>(hardware/firmware)"]
        end
        VDPA --> CTRL
        VDPA --> DATA
    end
    DRV -->|virtqueue| VDPA

5.2 vDPA vs SR-IOV vs vhost

FeaturevhostSR-IOVvDPA
PerformanceHighHighestHighest
ConfigurabilityFullLimitedFull (SW control plane)
Live migrationYesDifficultYes
Vendor lock-inNoneSomeNone (virtio standard)

5.3 Using vDPA

# Check vDPA devices
vdpa dev list

# Create a vDPA device
vdpa dev add mgmtdev pci/0000:03:00.0 name vdpa0

# Use with QEMU
qemu-system-x86_64 \
    -device virtio-net-pci,netdev=net0 \
    -netdev vhost-vdpa,id=net0,vhostdev=/dev/vhost-vdpa-0

6. Virtio Feature Negotiation

6.1 Feature Bits

Devices and drivers negotiate features during initialization:

FeatureBitDescription
VIRTIO_NET_F_CSUM0Host checksums
VIRTIO_NET_F_GSO6Generic segmentation offload
VIRTIO_NET_F_MRG_RXBUF15Merge receive buffers
VIRTIO_NET_F_MTU3Host sets MTU
VIRTIO_RING_F_INDIRECT_DESC28Indirect descriptors
VIRTIO_RING_F_EVENT_IDX29Interrupt suppression
VIRTIO_F_ORDER_PLATFORM36Platform ordering

6.2 Querying Features

# QEMU monitor
(qemu) info virtio
# virtio-net: features: 0x100000000 (indirect_desc)

# Guest-side
ethtool -i eth0
cat /sys/class/net/eth0/device/features

7. Multi-Queue Performance

7.1 Multi-Queue virtio-net

# QEMU: enable multi-queue
qemu-system-x86_64 \
    -device virtio-net-pci,netdev=net0,mq=on,vectors=8 \
    -netdev tap,id=net0,script=/etc/qemu-ifup,vhost=on,queues=4

# Guest: configure multi-queue
ethtool -L eth0 combined 4

7.2 Multi-Queue virtio-blk

# QEMU
qemu-system-x86_64 \
    -device virtio-blk-pci,drive=drive0,num-queues=4 \
    -drive file=disk.qcow2,format=qcow2,if=none,id=drive0

# Guest: verify
cat /sys/block/vda/queue/nr_requests
cat /sys/block/vda/mq/*/cpu_list

8. Performance Tuning

8.1 Guest-Side Tuning

# Increase virtqueue size
echo 1024 | sudo tee /sys/module/virtio_ring/parameters/max_queue_size

# Enable busy polling
echo 50 | sudo tee /sys/class/net/eth0/napi_defer_hard_irqs

# Use XDP for fast packet processing
ip link set dev eth0 xdpgeneric pass

# Tune interrupt coalescing
ethtool -C eth0 rx-usecs 50 tx-usecs 50

8.2 Host-Side Tuning

# Enable vhost for all virtio devices
# (default in modern QEMU)

# Use io_uring for virtio-blk
qemu-system-x86_64 \
    -device virtio-blk-pci,drive=drive0,iothread=iothread0 \
    -object iothread,id=iothread0 \
    -drive file=disk.qcow2,format=qcow2,if=none,id=drive0,aio=io_uring

# Pin vhost threads
taskset -c 0-3 qemu-system-x86_64 ...

8.3 Hugepages for Virtio

# Allocate hugepages (reduces TLB misses for virtio buffers)
echo 4096 | sudo tee /proc/sys/vm/nr_hugepages

# QEMU with hugepages
qemu-system-x86_64 \
    -object memory-backend-file,id=mem,size=4G,mem-path=/dev/hugepages,share=on \
    -numa node,memdev=mem \
    ...

8.4 Benchmark Comparison

# Network throughput (iperf3)
# Emulated e1000:   ~1 Gbps
# virtio-net:       ~10 Gbps
# virtio-net+vhost: ~18 Gbps
# vDPA:             ~20 Gbps (line rate on 25G NIC)

# Disk I/O (fio)
# Emulated IDE:     ~100 MB/s
# virtio-blk:       ~2 GB/s
# virtio-blk+io_uring: ~3 GB/s
# VFIO NVMe:        ~7 GB/s (native)

9. Virtio in Non-Linux Guests

9.1 Windows

Download drivers from Fedora VirtIO-Win:

# Attach driver ISO
qemu-system-x86_64 \
    -cdrom virtio-win.iso \
    -device virtio-net-pci,netdev=net0 \
    -device virtio-blk-pci,drive=drive0

9.2 FreeBSD

FreeBSD has native virtio drivers:

# In FreeBSD guest
kldload virtio
kldload if_vtnet
kldload virtio_blk

10. Virtio Specification

The Virtio specification is maintained by OASIS:

VersionKey Features
1.0Split virtqueues, basic devices
1.1Packed virtqueues, VIRTIO_F_ORDER_PLATFORM
1.2Admin virtqueue, virtio-mem, virtio-fs

The specification defines:

  • Device initialization and feature negotiation
  • Virtqueue layout and operation
  • Device-specific configuration (net, blk, scsi, etc.)
  • Transport bindings (PCI, MMIO, CCW)

11. virtio-fs — Shared Filesystem

virtio-fs provides host-guest file sharing using FUSE on the host side and a virtio transport. It replaces the older 9p/virtio transport with significantly better performance.

flowchart LR
    subgraph Guest
        APP[Application] --> VFS[Guest VFS]
        VFS --> VFSMOD[virtiofs driver]
    end
    subgraph Host
        VFSMOD -->|"virtqueue"| FUSE[FUSE daemon]
        FUSE --> HOSTFS[Host filesystem]
    end

Using virtio-fs

# Create shared directory on host
mkdir -p /tmp/shared

# Start QEMU with virtiofs
echo 2048 > /proc/sys/vm/nr_hugepages
qemu-system-x86_64 \
    -object memory-backend-file,id=mem,size=4G,mem-path=/dev/hugepages,share=on \
    -numa node,memdev=mem \
    -chardev socket,id=char0,path=/tmp/vhost-fs.sock \
    -device vhost-user-fs-pci,chardev=char0,tag=myfs \
    -numa node,memdev=mem \
    ...

# Mount in guest
mount -t virtiofs myfs /mnt/shared

# In /etc/fstab:
# myfs  /mnt/shared  virtiofs  defaults  0  0

virtio-fs vs 9p vs NFS

Featurevirtio-fs9p/virtioNFS
PerformanceExcellentPoorGood
POSIX complianceHighLimitedHigh
mmap supportYesNoYes
DAX supportYes (Linux 5.15+)NoNo
Setup complexityMediumLowMedium
SecuritySandboxedBasicNetwork

DAX (Direct Access) allows guest to mmap host files directly, bypassing the guest page cache — critical for large file access.

# Mount with DAX
cd /sys/kernel/tracing
mount -t virtiofs -o dax=always myfs /mnt/shared

12. virtio-vsock — Guest-Host Communication

virtio-vsock provides a socket-based communication channel between guest and host without network configuration:

flowchart LR
    subgraph Guest
        APP_G[Application] --> VSOCK_G[vsock socket]
    end
    subgraph Host
        APP_H[Application] --> VSOCK_H[vsock socket]
    end
    VSOCK_G -->|"virtqueue"| VSOCK_H

Using virtio-vsock

# QEMU: enable vhost-vsock
qemu-system-x86_64 \
    -device vhost-vsock-pci,guest-cid=3 \
    ...

# Guest: listen on vsock
socat VSOCK-LISTEN:1234,reuseaddr,fork EXEC:/bin/bash

# Host: connect to guest
socat VSOCK-CONNECT:3:1234 -

# CID (Context ID) identifies the guest
# CID 2 = host, CID 3+ = guests

# Guest side (C code)
# #include <linux/vm_sockets.h>
# int fd = socket(AF_VSOCK, SOCK_STREAM, 0);
# struct sockaddr_vm addr = {
#     .svm_family = AF_VSOCK,
#     .svm_cid = VMADDR_CID_HOST,
#     .svm_port = 1234,
# };
# connect(fd, (struct sockaddr *)&addr, sizeof(addr));

Use Cases for virtio-vsock

  • Guest management — SSH, serial console without network
  • File transfer — fast guest-host file copy
  • Agent communication — cloud-init, qemu-guest-agent
  • Development — debug servers without network forwarding

13. virtio-gpu — Virtual GPU

virtio-gpu provides GPU access for guests, supporting both 2D and 3D rendering:

# QEMU: enable virtio-gpu
qemu-system-x86_64 \
    -device virtio-gpu-pci \
    -display gtk,gl=on \
    ...

# For VirGL (3D acceleration via host GPU)
qemu-system-x86_64 \
    -device virtio-gpu-pci \
    -display gtk,gl=on \
    -spice gl=on,rendernode=/dev/dri/renderD128 \
    ...

# Guest: verify virtio-gpu
ls /dev/dri/
# card0  renderD128

glinfo | head -5
# Vendor:   Red Hat, Inc.
# Renderer: virgl
# Version:  4.5 (Core Profile)

14. Virtio Security Considerations

Attack Surface

flowchart TB
    subgraph Guest
        DRV[Virtio Driver] --> VQ[Virtqueue]
    end
    subgraph Host
        BACKEND[Backend Process] --> HOSTMEM[Host Memory]
    end
    VQ -->|"shared memory"| BACKEND
    
    subgraph Risks
        R1["Malicious guest driver"]
        R2["Shared memory corruption"]
        R3["Virtqueue descriptor manipulation"]
        R4["Resource exhaustion"]
    end

Security Best Practices

# 1. Use vhost (kernel backend) — smaller attack surface than QEMU
# vhost-net, vhost-scsi process requests in kernel

# 2. Memory isolation — IOMMU for virtio
qemu-system-x86_64 \
    -device virtio-net-pci,netdev=net0,disable-legacy=on,iommu_platform=on \
    ...

# 3. Input validation — backends must validate all guest-provided
#    descriptor chains, lengths, and flags

# 4. Resource limits — prevent guest from exhausting host resources
#    Limit virtqueue size, number of requests, memory usage

# 5. Seccomp sandboxing — restrict backend system calls
# QEMU uses seccomp by default: -sandbox on

# 6. Separate backends — run different device backends in different
#    processes/user IDs for isolation

CVE Mitigations

# Common virtio vulnerability patterns:
# - Out-of-bounds access via malicious descriptors
# - Integer overflow in descriptor chain parsing
# - Use-after-free in virtqueue management
# - Information leak via uninitialized memory in used ring

# Mitigations:
# - Kernel: hardening patches (KASAN, KCSAN, stack protector)
# - QEMU: sandbox mode, -sandbox on
# - IOMMU: DMA remapping prevents guest DMA attacks
# - Disable legacy: disable-legacy=on (forces modern virtio)

15. Virtio Transport Bindings

PCI Transport (Most Common)

# virtio PCI devices appear in lspci
lspci | grep -i virtio
# 00:03.0 Ethernet controller: Red Hat, Inc. Virtio network device
# 00:04.0 SCSI storage controller: Red Hat, Inc. Virtio SCSI
# 00:05.0 Unclassified device: Red Hat, Inc. Virtio filesystem

# PCI configuration
lspci -v -s 00:03.0
# Capabilities: [98] MSI-X: Enable+ Count=5

# PCI feature bits
cat /sys/bus/pci/devices/0000:00:03.0/features
# Shows negotiated feature bits in hex

MMIO Transport (Embedded/ARM)

# Used for embedded and ARM systems without PCI
# Device Tree describes virtio-mmio devices

# QEMU with virtio-mmio
qemu-system-aarch64 \
    -machine virt \
    -device virtio-net-device,netdev=net0 \
    -device virtio-blk-device,drive=drive0 \
    ...

# Device tree entry:
# virtio_mmio@a000000 {
#     compatible = "virtio,mmio";
#     reg = <0x0a000000 0x200>;
#     interrupts = <0 16 1>;
# };

CCW Transport (s390x)

# Channel Command Word (CCW) transport for IBM Z/s390x
qemu-system-s390x \
    -device virtio-net-ccw,netdev=net0 \
    ...

16. Virtio in the Cloud

# Major cloud providers use virtio:
# - AWS: Enhanced Networking (ENA) + virtio
# - GCP: virtio-net, virtio-scsi
# - Azure: Hyper-V devices (similar concepts)
# - OpenStack: virtio for KVM guests

# Verify virtio in cloud VM
lspci | grep -i virtio
# On AWS: also check for ENA (enhanced network adapter)
ethtool -i eth0
# driver: vif_net on xen / virtio_net on KVM

Further Reading