The Virtqueue implementation of QEMU

Source: Internet
Author: User
Tags bool

As with the kernel, QEMU also needs to support the definition of Virtqueue,virtqueue as follows

#define VIRTIO_PCI_VRING_ALIGN 4096 typedef struct VRINGDESC {uint64_t addr;
    uint32_t Len;
    uint16_t flags;
Uint16_t Next;

} Vringdesc;
    typedef struct VRINGAVAIL {uint16_t flags;
    uint16_t idx;
uint16_t Ring[0];

} Vringavail;
    typedef struct VRINGUSEDELEM {uint32_t id;
uint32_t Len;

} Vringusedelem;
    typedef struct VRINGUSED {uint16_t flags;
    uint16_t idx;
Vringusedelem Ring[0];

} vringused;
    typedef struct VRING {unsigned int num;
    unsigned int align;
    Hwaddr desc;
    HWADDR avail;
HWADDR used;

} vring;  struct Virtqueue {vring vring;  /* vring Meta Data */HWADDR PA;
    /* Vring The actual memory address */uint16_t last_avail_idx;

    /* Last used index value we had signalled on */uint16_t signalled_used;

    /* Last used index value we had signalled on */bool signalled_used_valid; /* Notification enabled?

    */BOOL notification;

    uint16_t Queue_index;

    int inuse;
    uint16_t Vector; void(*handle_output)
    (Virtiodevice *vdev, Virtqueue *vq);
    Virtiodevice *vdev;
    Eventnotifier Guest_notifier;
Eventnotifier Host_notifier; };
You can see the definition of the vring structure, and QEMU and the kernel are consistent on the ABI. Virtqueue_init is used to initialize vring metadata, while QEMU provides a series of interfaces to read and write vring different members, e.g.

Static inline uint64_t vring_desc_addr (virtiodevice *vdev, hwaddr desc_pa, int i)
    /* Read the addr address of the first Vringdesc */{hwaddr pa;
    PA = desc_pa + sizeof (VRINGDESC) * i + offsetof (vringdesc, addr);
Return Virtio_ldq_phys (Vdev, PA);  } Static inline uint32_t Vring_desc_len (virtiodevice *vdev, hwaddr desc_pa, int i)/* Read the first vringdesc of Len */{HWADDR
    Pa
    PA = desc_pa + sizeof (VRINGDESC) * i + offsetof (Vringdesc, Len);
Return Virtio_ldl_phys (Vdev, PA);
                                        } Static inline uint16_t vring_desc_flags (virtiodevice *vdev, hwaddr desc_pa,/* Read the "I" VRINGDESC's flags */
    int i) {hwaddr pa;
    PA = desc_pa + sizeof (VRINGDESC) * i + offsetof (VRINGDESC, flags);
Return Virtio_lduw_phys (Vdev, PA);
                                       } Static inline uint16_t vring_desc_next (virtiodevice *vdev, hwaddr DESC_PA,/* reads the next index of I Vringdesc */
    int i) {hwaddr pa; PA = desc_pa + sizeof (VRINGDESC) * I + Offsetof (Vringdesc, next);
Return Virtio_lduw_phys (Vdev, PA);
    } Static inline uint16_t vring_avail_flags (virtqueue *vq)/* Read the flags of the avail ring */{hwaddr pa;
    PA = vq->vring.avail + offsetof (vringavail, flags);
Return Virtio_lduw_phys (Vq->vdev, PA);
    } Static inline uint16_t vring_avail_idx (virtqueue *vq)/* Read the idx of the avail ring */{hwaddr pa;
    PA = vq->vring.avail + offsetof (vringavail, IDX);
Return Virtio_lduw_phys (Vq->vdev, PA);
    } Static inline uint16_t vring_avail_ring (virtqueue *vq, int i)/* Read the first I idx of the avail ring */{hwaddr pa;
    PA = vq->vring.avail + offsetof (Vringavail, ring[i]);
Return Virtio_lduw_phys (Vq->vdev, PA); } Static inline uint16_t vring_used_event (virtqueue *vq)/* read avail saved in USED_EVENT_IDX ring */{return vring_avail_r
ING (VQ, vq->vring.num); } static inline void vring_used_ring_id (virtqueue *vq, int i, uint32_t val)/* Modify the ID of the used ring */{Elem PA
    ; PA = vq->vring.used + offsetOf (vringused, ring[i].id);
Virtio_stl_phys (Vq->vdev, PA, Val); } static inline void Vring_used_ring_len (virtqueue *vq, int i, uint32_t val)/* Modify Len/{used in Elem ring] */HWADDR
    Pa
    PA = vq->vring.used + offsetof (vringused, Ring[i].len);
Virtio_stl_phys (Vq->vdev, PA, Val);
    } static uint16_t Vring_used_idx (Virtqueue *vq)/* reads the IDX in the used ring */{hwaddr pa;
    PA = vq->vring.used + offsetof (vringused, IDX);
Return Virtio_lduw_phys (Vq->vdev, PA);
    } static inline void Vring_used_idx_set (Virtqueue *vq, uint16_t val)/* Set the IDX in used ring */{hwaddr pa;
    PA = vq->vring.used + offsetof (vringused, IDX);
Virtio_stw_phys (Vq->vdev, PA, Val); } static inline void Vring_used_flags_set_bit (virtqueue *vq, int mask)/* Set the bit bits of flags in the used ring */{Virtiodevice *
    Vdev = vq->vdev;
    Hwaddr PA;
    PA = vq->vring.used + offsetof (vringused, flags);
Virtio_stw_phys (Vdev, PA, Virtio_lduw_phys (Vdev, PA) | mask); } Static Inline VOID Vring_used_flags_unset_bit (virtqueue *vq, int mask)/* Clean the bit bit of flags in used ring */{Virtiodevice *vdev = VQ->VD
    Ev
    Hwaddr PA;
    PA = vq->vring.used + offsetof (vringused, flags);
Virtio_stw_phys (Vdev, PA, Virtio_lduw_phys (Vdev, PA) & ~mask); }
The backend also provides a range of interfaces to handle used ring,e.g.

The Virtqueue_pop is primarily used to find the buffer added in the available ring from descriptor table, which is the new addition of the guest and lets the backend process buffer

int Virtqueue_pop (Virtqueue *vq, virtqueueelement *elem) {unsigned int i, head, Max;
    Hwaddr DESC_PA = vq->vring.desc;

    Virtiodevice *vdev = vq->vdev; if (!virtqueue_num_heads (VQ, VQ->LAST_AVAIL_IDX))/* vs Vring_avail_idx (VQ) and vq->last_avail_idx, Judge VQ avail Whether the IDX has increased/return 0; /* If 0 indicates that the avail ring does not have a new buffer, there is no need to process the direct return */* When we start there is none of the either input nor output.

    */elem->out_num = Elem->in_num = 0;

    max = vq->vring.num; i = head = Virtqueue_get_head (VQ, vq->last_avail_idx++); /* Starting from LAST_AVAIL_IDX, the avail RING points to the vring DESC Entry Index */if (Vdev->guest_features & (1 << Virtio_ring_f_ev ENT_IDX) {/* <span style= "font-family:arial, Helvetica, Sans-serif;"  > If guest enable Virtio_ring_f_event_idx */</span> vring_avail_event (VQ, Vring_avail_idx (VQ)); /* Set AVAIL_EVENT_IDX to the latest avail ring IDX value */} if (Vring_desc_flags (Vdev, Desc_pa, i) & Vring_desc_f_indirect) {/* sectionThe flags of the I DESC if enable Vring_desc_f_indirect */if (Vring_desc_len (Vdev, DESC_PA, i)% sizeof (VRINGDESC)) {/* Indir
            The desc len of the ECT must be an integer multiple of sizeof (VRINGDESC) */Error_report ("Invalid size for indirect buffer table");
        Exit (1); }/* Loop over the indirect descriptor Table */max = Vring_desc_len (Vdev, DESC_PA, i)/sizeof (VRINGDESC );  /* Maximum traverse max Vringdesc */Desc_pa = VRING_DESC_ADDR (Vdev, DESC_PA, i);
    /* Desc_pa point to the Vringdesc array that indirect points to */i = 0; }/* Collect all the descriptors */do {/* Iterate through VRINGDESC items, and addr, Len into the virtqueueelement structure */struct IOVE

        c *SG; if (Vring_desc_flags (Vdev, Desc_pa, i) & Vring_desc_f_write) {if (Elem->in_num >= array_size (elem-&
                GT;IN_SG) {Error_report ("Too many write descriptors in indirect table");
            Exit (1);
        } Elem->in_addr[elem->in_num] = Vring_desc_addr (Vdev, DESC_PA, i);    SG = &elem->in_sg[elem->in_num++];  } else {if (Elem->out_num >= array_size (ELEM-&GT;OUT_SG)) {Error_report ("Too many read
                Descriptors in indirect table ");
            Exit (1);
            } Elem->out_addr[elem->out_num] = Vring_desc_addr (Vdev, DESC_PA, i);
        SG = &elem->out_sg[elem->out_num++]; } Sg->iov_len = Vring_desc_len (Vdev, DESC_PA, i); /* The iov_base portion of the SG is stored in in_addr, OUT_ADDR */* If we ' ve got too many, that implies a descriptor loop.
            */if ((Elem->in_num + elem->out_num) > Max) {error_report ("looped descriptor");
        Exit (1);  }} while ((i = Virtqueue_next_desc (Vdev, Desc_pa, I, max))! = max); /* Traverse VRINGDESC until Max */* Now maps what we have collected */Virtqueue_map_sg (ELEM-&GT;IN_SG, ELEM-&GT;IN_ADDR, E Lem->in_num, 1); /* Map the address into Hva via Cpu_physical_memory_map and deposit sg->iov_base */VirtquEue_map_sg (Elem->out_sg, elem->out_addr, Elem->out_num, 0); Elem->index = head;

    /* Index set to VRINGDESC Head Index */vq->inuse++;
    Trace_virtqueue_pop (VQ, Elem, Elem->in_num, elem->out_num);  return Elem->in_num + elem->out_num; /* Return Virtqueue_pop total number of VRINGDESC, */}
Virtqueue_fill when the Virtio host end (qemu/vhost) finishes processing the guest into buffer in the avail ring, the buffer is unmapped and placed in the used ring

void Virtqueue_fill (Virtqueue *vq, const virtqueueelement *elem, unsigned int len, unsigned int idx) {
    unsigned int offset;

    int i;

    Trace_virtqueue_fill (VQ, Elem, Len, idx);
    offset = 0; for (i = 0; i < elem->in_num; i++) {/* Cancel sg_in HVA Memory Map */size_t size = MIN (Len-offset, Elem->in_sg[i]

        . Iov_len);
                                  Cpu_physical_memory_unmap (Elem->in_sg[i].iov_base, Elem->in_sg[i].iov_len,

        1, size);
    Offset + = size; } for (i = 0; i < elem->out_num; i++)/* Cancel Sg_out HVA Memory map */Cpu_physical_memory_unmap (ELEM-&GT;OUT_SG [I].iov_base, Elem->out_sg[i].iov_len, 0, elem->

    Out_sg[i].iov_len); IDX = (idx + vring_used_idx (VQ))% vq->vring.num; /* Calculate the new used ring IDX value by using the IDX + USED_EVENT_IDX to vring.num modulo */* Get A pointer to the next entry in the used ring.
*/    VRING_USED_RING_ID (VQ, IDX, elem->index);
/* Configure the contents of the new used ring entry, the ID is the index of the VRINGDESC pointed to by Elem->index, Len is its length */Vring_used_ring_len (VQ, IDX, Len); }
Virtqueue_flush for updating the idx of the user ring

void Virtqueue_flush (Virtqueue *vq, unsigned int count)
{
    uint16_t old, new;
    /* Make sure buffer is written before we update index. *
    /SMP_WMB ();
    Trace_virtqueue_flush (VQ, count);
    Old = Vring_used_idx (VQ);
    New = old + count;
    Vring_used_idx_set (VQ, new);
    Vq->inuse-= count;
    if (Unlikely (int16_t) (new-vq->signalled_used) < (uint16_t) (new-old))
        Vq->signalled_used_valid = false;  /* Whether to trigger Used_event */
}






Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.