您的位置:首页 > 其它

virtio 1.0 简介

2016-03-10 17:09 357 查看
Introduction

This article aims to offer a different view of virtio devices from most of the other articles available online. It is neither a complete reference to the virtio 1.0 spec, nor a high-level overview, but rather aims at providing the reader with an idea of how
a virtio driver and device implementation work and communicate with each other, through the use of diagrams. That information could then be used as a starting point in cases where it becomes necessary to delve into a virtio device implementation's code for
troubleshooting purposes. Throughout the text, we will assume PCI is the transport mechanism used, ignoring MMIO or channel I/O, and choose virtio-net as our example device type. Accordingly, all the elements that are device type-specific in the figures below pertain
to virtio-net devices.

Device Discovery

Below is a diagram that describes what the configuration space of a virtio 1.0 net pci device would look like. Note, for legacy devices, It could be guest native endianess instead of PCI’s little-endian.
register(offset)bits 31-24bits 23-16bits 15-8bits 7-0
00Device ID (0x1041)Vendor ID (0x1a64)
......
08Class Code(2)......Revision ID(1)
......
10IO BAR
14Memory BAR
......
2CSubsystemID(0x0001)Subsystem Vendor ID(0x1af4)
.....
34......Capabilities Pointer
......
Virtio capability | Virtio capability | Virtio capability | Virtio capability
......
The Capabilities Pointer points to the virtio capability chain. Each virtio capability area has an offset value that is relative to the IO/Memory BAR. Through that they expose virtio common and device specific configuration
area, queue notify location, and ISR status to the driver. Below is a typical layout for the IO/Memory region mapped by the BAR. Again, for legacy device, the layout of virtio header and device specific area is quite different. For detail of the layout for
legacy device, please refer to virtio 1.0 spec.
<Common Configuration>

device_feature_select device_feature driver_feature_select driver_feature msix_config num_queues device_status config_generation

queue_select queue_size queue_msix_vector queue_enable queue_notify_off queue_desc queue_avail queue_used
<Queue Notify>

Queue_1_Notify Queue_2_Notify …...
<ISR status>

Bits | 0 | 1 | 2 to 31

Purpose | Queue Interrupt | Device Configuration Interrupt | Reserved
<Device configuration>

Mac status max_virtqueue_pairs
Device Configuration

The driver needs to negotiate for the supported features with the device. And another important thing is to setup the virtqueue. It writes the virtqueue index to the queue_select field in common configuration section, and reads the virtqueue size from the queue_size
field there. Then it allocates the descriptor table, available ring, and used ring in a single page, and writes the address of each part to queue_desc, queue_avail, and queue_used fields. So the device can find them.

Virtqueue Data Structure

Each virtqueue consists of three parts – descriptor table, available ring, and used ring. Simple virtio net device has one virtqueue for transmit and one for receive. It can also have multiple send/receive virtqueue pairs depends on the setup[3]. Each queue
has a 16-bit queue size parameter, which sets the number of entries and implies the total size of the queue according to '2.4 Virtqueues' from virtio 1.0 spec.

'The descriptor table refers to the buffers the driver is using for the device. The addresses are physical addresses, and the buffers can be chained via the next field.' (from '2.4.4
The Virtqueue Descriptor Table') Actual descriptor buffers are allocated in the guest, and the addr below is GPA. KVM can locate it because it maintains a mapping between GPA and HVA. Below is a descriptor table entry.
IndexAddrLenFlagsNext
The available ring refers to the descriptor chains the driver is offering the device. Each ring entry refers to the head of a descriptor chain, written by the driver and read by the device.
Flagidxring[]
'The used ring is where the device returns buffers once it is done with them. It is only written to by the device, and read by the driver. '(from '2.4.6 The Virtqueue Used Ring')
Flagidxvring_used_elem ring[]
Device Operation

Supplying Buffers to The Device:

The buffers are put into free descriptor in the descriptor table, and multiply buffers can be chained together throught the next field of the descriptor.
The index of the first descriptor in the chain is then put into the next ring entry (pointed by idx field) of the available ring. And the drive needs to increase the idx field of the available ring.
If notification is on, the driver notifies the device of the new available buffers by writing to the queue notify entry in the queue notify section.

Below is an example where a chain of two buffers is placed onto the available ring.

Descriptor table:
IndexAddrLenFlagsNext
00x80000000000000004096NEXT1
10x8000000000040000128WRITE0
......
Available ring:
0......
Receiving Used Buffers From The Device:

The descriptor number is written to the next field (pointed by idx field) in the used ring by the device. And the used ring index (idx field) is updated by the device as well.
The device sends an interrupt to guest if required according to feature negotiation and per ring flag in available ring structure.

Below is an example where the above available ring is consumed and returned back.

Descriptor table:
IndexAddrLenFlagsNext
00x80000000000000004096NEXT1
10x8000000000040000128WRITE0
......
Used ring:
0......
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: