int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)

{

    unsigned int i, head, max;

    target_phys_addr_t desc_pa = vq->vring.desc;



    if (!virtqueue_num_heads(vq, vq->last_avail_idx))

        return 0;



    

    elem->out_num = elem->in_num = 0;



    max = vq->vring.num;



    i = head = virtqueue_get_head(vq, vq->last_avail_idx++);

    if (vq->vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) {

        vring_avail_event(vq, vring_avail_idx(vq));




    if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_INDIRECT) {

        if (vring_desc_len(desc_pa, i) % sizeof(VRingDesc)) {

            error_report("Invalid size for indirect buffer table");





        

        max = vring_desc_len(desc_pa, i) / sizeof(VRingDesc);

        desc_pa = vring_desc_addr(desc_pa, i);

        i = 0;




    

    do {

        struct iovec *sg;



        if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_WRITE) {





            elem->in_addr[elem->in_num] = vring_desc_addr(desc_pa, i);

            sg = &elem->in_sg[elem->in_num++];

        } else {





            elem->out_addr[elem->out_num] = vring_desc_addr(desc_pa, i);

            sg = &elem->out_sg[elem->out_num++];




        sg->iov_len = vring_desc_len(desc_pa, i);



        

        if ((elem->in_num + elem->out_num) > max) {

            error_report("Looped descriptor");



    } while ((i = virtqueue_next_desc(desc_pa, i, max)) != max);



    

    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);



    elem->index = head;



    vq->inuse++;



    trace_virtqueue_pop(vq, elem, elem->in_num, elem->out_num);

    return elem->in_num + elem->out_num;
