For the question of the review document that day, and looked at the code, the Virtio send and receive queue is created in the guest's front-end driver
Guest front-end driver, take network devices as an example: After VIRTIO-NET:PCI discovery, the transceiver queue is allocated via the PCI bus
static int virtnet_probe (Structvirtio_device *vdev)
{
......
/*
* Initialize Virtqueue
* Create and initialize Send/Receive queues
*/
Err = Init_vqs (vi);
......
}
/* Create and initialize send/Receive queues */
static int Init_vqs (struct virtnet_info*vi)
{
/* Assign */
ret = Virtnet_alloc_queues (vi);
if (ret)
Goto err;
/* Create vring*/with Find Vqs
ret = Virtnet_find_vqs (vi);
if (ret)
Goto Err_free;
......
}
/* Create vring*/with Find Vqs
static int Virtnet_find_vqs (Structvirtnet_info *vi)
{
......
/* Call is Vp_find_vqs, really create the VIRTQUEUE internal structure and assign the address, and tell the address to the backend QEMU driver */
ret = Vi->vdev->config->find_vqs (Vi->vdev,total_vqs, Vqs, callbacks, names);
......
}
VIRTIO PCI bus
static int Vp_find_vqs (struct virtio_device*vdev, unsigned NVQs,
struct Virtqueue *vqs[],
vq_callback_t *callbacks[],
const CHAR *names[])
{
int err;
Err = Vp_try_to_find_vqs (Vdev,nvqs, Vqs, callbacks, names, true, true);
Err = Vp_try_to_find_vqs (Vdev,nvqs, Vqs, callbacks, names, true, false);
Return Vp_try_to_find_vqs (Vdev,nvqs, Vqs, callbacks, names, false, false);
}
static int Vp_try_to_find_vqs ()
{
......
/* The core is SETUP_VQ () */
Vqs[i] = SETUP_VQ (Vdev,i, Callbacks[i], names[i], Msix_vec);
......
}
static struct Virtqueue *setup_vq (structvirtio_device *vdev, unsigned index,
void (*callback) (struct virtqueue *vq),
const Char *name,
U16 Msix_vec)
{
/* Set the address of the Virtio vring to the backend qemu*/
Iowrite32 (Virt_to_phys (info->queue) >> virtio_pci_queue_addr_shift, Vp_dev->ioaddr +virtio_pci_queue_ PFN);
}
IO read/write is intercepted by backend QEMU for simulation
Qemu back-end driver
static void Virtio_ioport_write (Void*opaque, uint32_t addr, uint32_t val)
{
......
Switch (addr) {
Case VIRTIO_PCI_QUEUE_PFN:
PA = (hwaddr) val << virtio_pci_queue_addr_shift;
......
Virtio_queue_set_addr (Vdev,vdev->queue_sel, PA);
......
}
There is also a detail problem, the front-end driver writes should be qeueu GPA "IOWRITE32 (Virt_to_phys (info->queue) >> virtio_pci_queue_addr_shift,vp_dev- >IOADDR + VIRTIO_PCI_QUEUE_PFN); "
This was intercepted by the backend qemu, how does QEMU directly use GPA? Where to complete the conversion of Gpa->hva?
This is the conversion when QEMU takes the message from the Virtqueue.
QEMU code, after receiving the Virtio notification, the message is removed from the Shared queue via Virtqueue_pop
Intvirtqueue_pop (Virtqueue *vq, virtqueueelement *elem)
{
/*now Map What do we have collected */
Virtqueue_map_sg (Elem->in_sg,elem->in_addr, Elem->in_num, 1);
Virtqueue_map_sg (elem->out_sg,elem->out_addr, elem->out_num, 0);
}
Voidvirtqueue_map_sg ()
{
for (i = 0; i < Num_sg; i++) {
len = Sg[i].iov_len;
Sg[i].iov_base =cpu_physical_memory_map (Addr[i], &len, is_write);
if (sg[i].iov_base = = NULL | | Len! = sg[i].iov_len) {
Error_report ("Virtio:trying to map MMIO memory");
Exit (1);
}
}
}
/* Complete a guest's physical address conversion from GPA to HVA */
Void*cpu_physical_memory_map (hwaddr addr,
Hwaddr *plen,
int is_write)
{
Return Address_space_map (&address_space_memory,addr, Plen, is_write);
}
Thanks
Feng
Virtio Official spec in
Http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html