dataplane: change vring API to use VirtQueueElement
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
		
							parent
							
								
									781c117f37
								
							
						
					
					
						commit
						8c1b566fd1
					
				@ -35,7 +35,7 @@ enum {
 | 
				
			|||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    struct iocb iocb;               /* Linux AIO control block */
 | 
					    struct iocb iocb;               /* Linux AIO control block */
 | 
				
			||||||
    QEMUIOVector *inhdr;            /* iovecs for virtio_blk_inhdr */
 | 
					    QEMUIOVector *inhdr;            /* iovecs for virtio_blk_inhdr */
 | 
				
			||||||
    unsigned int head;              /* vring descriptor index */
 | 
					    VirtQueueElement *elem;         /* saved data from the virtqueue */
 | 
				
			||||||
    struct iovec *bounce_iov;       /* used if guest buffers are unaligned */
 | 
					    struct iovec *bounce_iov;       /* used if guest buffers are unaligned */
 | 
				
			||||||
    QEMUIOVector *read_qiov;        /* for read completion /w bounce buffer */
 | 
					    QEMUIOVector *read_qiov;        /* for read completion /w bounce buffer */
 | 
				
			||||||
} VirtIOBlockRequest;
 | 
					} VirtIOBlockRequest;
 | 
				
			||||||
@ -96,7 +96,7 @@ static void complete_request(struct iocb *iocb, ssize_t ret, void *opaque)
 | 
				
			|||||||
        len = 0;
 | 
					        len = 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    trace_virtio_blk_data_plane_complete_request(s, req->head, ret);
 | 
					    trace_virtio_blk_data_plane_complete_request(s, req->elem->index, ret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (req->read_qiov) {
 | 
					    if (req->read_qiov) {
 | 
				
			||||||
        assert(req->bounce_iov);
 | 
					        assert(req->bounce_iov);
 | 
				
			||||||
@ -118,12 +118,12 @@ static void complete_request(struct iocb *iocb, ssize_t ret, void *opaque)
 | 
				
			|||||||
     * written to, but for virtio-blk it seems to be the number of bytes
 | 
					     * written to, but for virtio-blk it seems to be the number of bytes
 | 
				
			||||||
     * transferred plus the status bytes.
 | 
					     * transferred plus the status bytes.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    vring_push(&s->vring, req->head, len + sizeof(hdr));
 | 
					    vring_push(&s->vring, req->elem, len + sizeof(hdr));
 | 
				
			||||||
 | 
					    req->elem = NULL;
 | 
				
			||||||
    s->num_reqs--;
 | 
					    s->num_reqs--;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void complete_request_early(VirtIOBlockDataPlane *s, unsigned int head,
 | 
					static void complete_request_early(VirtIOBlockDataPlane *s, VirtQueueElement *elem,
 | 
				
			||||||
                                   QEMUIOVector *inhdr, unsigned char status)
 | 
					                                   QEMUIOVector *inhdr, unsigned char status)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    struct virtio_blk_inhdr hdr = {
 | 
					    struct virtio_blk_inhdr hdr = {
 | 
				
			||||||
@ -134,26 +134,26 @@ static void complete_request_early(VirtIOBlockDataPlane *s, unsigned int head,
 | 
				
			|||||||
    qemu_iovec_destroy(inhdr);
 | 
					    qemu_iovec_destroy(inhdr);
 | 
				
			||||||
    g_slice_free(QEMUIOVector, inhdr);
 | 
					    g_slice_free(QEMUIOVector, inhdr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    vring_push(&s->vring, head, sizeof(hdr));
 | 
					    vring_push(&s->vring, elem, sizeof(hdr));
 | 
				
			||||||
    notify_guest(s);
 | 
					    notify_guest(s);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Get disk serial number */
 | 
					/* Get disk serial number */
 | 
				
			||||||
static void do_get_id_cmd(VirtIOBlockDataPlane *s,
 | 
					static void do_get_id_cmd(VirtIOBlockDataPlane *s,
 | 
				
			||||||
                          struct iovec *iov, unsigned int iov_cnt,
 | 
					                          struct iovec *iov, unsigned int iov_cnt,
 | 
				
			||||||
                          unsigned int head, QEMUIOVector *inhdr)
 | 
					                          VirtQueueElement *elem, QEMUIOVector *inhdr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    char id[VIRTIO_BLK_ID_BYTES];
 | 
					    char id[VIRTIO_BLK_ID_BYTES];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Serial number not NUL-terminated when shorter than buffer */
 | 
					    /* Serial number not NUL-terminated when shorter than buffer */
 | 
				
			||||||
    strncpy(id, s->blk->serial ? s->blk->serial : "", sizeof(id));
 | 
					    strncpy(id, s->blk->serial ? s->blk->serial : "", sizeof(id));
 | 
				
			||||||
    iov_from_buf(iov, iov_cnt, 0, id, sizeof(id));
 | 
					    iov_from_buf(iov, iov_cnt, 0, id, sizeof(id));
 | 
				
			||||||
    complete_request_early(s, head, inhdr, VIRTIO_BLK_S_OK);
 | 
					    complete_request_early(s, elem, inhdr, VIRTIO_BLK_S_OK);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int do_rdwr_cmd(VirtIOBlockDataPlane *s, bool read,
 | 
					static int do_rdwr_cmd(VirtIOBlockDataPlane *s, bool read,
 | 
				
			||||||
                       struct iovec *iov, unsigned int iov_cnt,
 | 
					                       struct iovec *iov, unsigned iov_cnt,
 | 
				
			||||||
                       long long offset, unsigned int head,
 | 
					                       long long offset, VirtQueueElement *elem,
 | 
				
			||||||
                       QEMUIOVector *inhdr)
 | 
					                       QEMUIOVector *inhdr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    struct iocb *iocb;
 | 
					    struct iocb *iocb;
 | 
				
			||||||
@ -186,19 +186,20 @@ static int do_rdwr_cmd(VirtIOBlockDataPlane *s, bool read,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /* Fill in virtio block metadata needed for completion */
 | 
					    /* Fill in virtio block metadata needed for completion */
 | 
				
			||||||
    VirtIOBlockRequest *req = container_of(iocb, VirtIOBlockRequest, iocb);
 | 
					    VirtIOBlockRequest *req = container_of(iocb, VirtIOBlockRequest, iocb);
 | 
				
			||||||
    req->head = head;
 | 
					    req->elem = elem;
 | 
				
			||||||
    req->inhdr = inhdr;
 | 
					    req->inhdr = inhdr;
 | 
				
			||||||
    req->bounce_iov = bounce_iov;
 | 
					    req->bounce_iov = bounce_iov;
 | 
				
			||||||
    req->read_qiov = read_qiov;
 | 
					    req->read_qiov = read_qiov;
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int process_request(IOQueue *ioq, struct iovec iov[],
 | 
					static int process_request(IOQueue *ioq, VirtQueueElement *elem)
 | 
				
			||||||
                           unsigned int out_num, unsigned int in_num,
 | 
					 | 
				
			||||||
                           unsigned int head)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    VirtIOBlockDataPlane *s = container_of(ioq, VirtIOBlockDataPlane, ioqueue);
 | 
					    VirtIOBlockDataPlane *s = container_of(ioq, VirtIOBlockDataPlane, ioqueue);
 | 
				
			||||||
    struct iovec *in_iov = &iov[out_num];
 | 
					    struct iovec *iov = elem->out_sg;
 | 
				
			||||||
 | 
					    struct iovec *in_iov = elem->in_sg;
 | 
				
			||||||
 | 
					    unsigned out_num = elem->out_num;
 | 
				
			||||||
 | 
					    unsigned in_num = elem->in_num;
 | 
				
			||||||
    struct virtio_blk_outhdr outhdr;
 | 
					    struct virtio_blk_outhdr outhdr;
 | 
				
			||||||
    QEMUIOVector *inhdr;
 | 
					    QEMUIOVector *inhdr;
 | 
				
			||||||
    size_t in_size;
 | 
					    size_t in_size;
 | 
				
			||||||
@ -229,29 +230,29 @@ static int process_request(IOQueue *ioq, struct iovec iov[],
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    switch (outhdr.type) {
 | 
					    switch (outhdr.type) {
 | 
				
			||||||
    case VIRTIO_BLK_T_IN:
 | 
					    case VIRTIO_BLK_T_IN:
 | 
				
			||||||
        do_rdwr_cmd(s, true, in_iov, in_num, outhdr.sector * 512, head, inhdr);
 | 
					        do_rdwr_cmd(s, true, in_iov, in_num, outhdr.sector * 512, elem, inhdr);
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    case VIRTIO_BLK_T_OUT:
 | 
					    case VIRTIO_BLK_T_OUT:
 | 
				
			||||||
        do_rdwr_cmd(s, false, iov, out_num, outhdr.sector * 512, head, inhdr);
 | 
					        do_rdwr_cmd(s, false, iov, out_num, outhdr.sector * 512, elem, inhdr);
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    case VIRTIO_BLK_T_SCSI_CMD:
 | 
					    case VIRTIO_BLK_T_SCSI_CMD:
 | 
				
			||||||
        /* TODO support SCSI commands */
 | 
					        /* TODO support SCSI commands */
 | 
				
			||||||
        complete_request_early(s, head, inhdr, VIRTIO_BLK_S_UNSUPP);
 | 
					        complete_request_early(s, elem, inhdr, VIRTIO_BLK_S_UNSUPP);
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    case VIRTIO_BLK_T_FLUSH:
 | 
					    case VIRTIO_BLK_T_FLUSH:
 | 
				
			||||||
        /* TODO fdsync not supported by Linux AIO, do it synchronously here! */
 | 
					        /* TODO fdsync not supported by Linux AIO, do it synchronously here! */
 | 
				
			||||||
        if (qemu_fdatasync(s->fd) < 0) {
 | 
					        if (qemu_fdatasync(s->fd) < 0) {
 | 
				
			||||||
            complete_request_early(s, head, inhdr, VIRTIO_BLK_S_IOERR);
 | 
					            complete_request_early(s, elem, inhdr, VIRTIO_BLK_S_IOERR);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            complete_request_early(s, head, inhdr, VIRTIO_BLK_S_OK);
 | 
					            complete_request_early(s, elem, inhdr, VIRTIO_BLK_S_OK);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    case VIRTIO_BLK_T_GET_ID:
 | 
					    case VIRTIO_BLK_T_GET_ID:
 | 
				
			||||||
        do_get_id_cmd(s, in_iov, in_num, head, inhdr);
 | 
					        do_get_id_cmd(s, in_iov, in_num, elem, inhdr);
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
@ -267,29 +268,8 @@ static void handle_notify(EventNotifier *e)
 | 
				
			|||||||
    VirtIOBlockDataPlane *s = container_of(e, VirtIOBlockDataPlane,
 | 
					    VirtIOBlockDataPlane *s = container_of(e, VirtIOBlockDataPlane,
 | 
				
			||||||
                                           host_notifier);
 | 
					                                           host_notifier);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* There is one array of iovecs into which all new requests are extracted
 | 
					    VirtQueueElement *elem;
 | 
				
			||||||
     * from the vring.  Requests are read from the vring and the translated
 | 
					    int ret;
 | 
				
			||||||
     * descriptors are written to the iovecs array.  The iovecs do not have to
 | 
					 | 
				
			||||||
     * persist across handle_notify() calls because the kernel copies the
 | 
					 | 
				
			||||||
     * iovecs on io_submit().
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * Handling io_submit() EAGAIN may require storing the requests across
 | 
					 | 
				
			||||||
     * handle_notify() calls until the kernel has sufficient resources to
 | 
					 | 
				
			||||||
     * accept more I/O.  This is not implemented yet.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    struct iovec iovec[VRING_MAX];
 | 
					 | 
				
			||||||
    struct iovec *end = &iovec[VRING_MAX];
 | 
					 | 
				
			||||||
    struct iovec *iov = iovec;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* When a request is read from the vring, the index of the first descriptor
 | 
					 | 
				
			||||||
     * (aka head) is returned so that the completed request can be pushed onto
 | 
					 | 
				
			||||||
     * the vring later.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * The number of hypervisor read-only iovecs is out_num.  The number of
 | 
					 | 
				
			||||||
     * hypervisor write-only iovecs is in_num.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    int head;
 | 
					 | 
				
			||||||
    unsigned int out_num = 0, in_num = 0;
 | 
					 | 
				
			||||||
    unsigned int num_queued;
 | 
					    unsigned int num_queued;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    event_notifier_test_and_clear(&s->host_notifier);
 | 
					    event_notifier_test_and_clear(&s->host_notifier);
 | 
				
			||||||
@ -298,30 +278,31 @@ static void handle_notify(EventNotifier *e)
 | 
				
			|||||||
        vring_disable_notification(s->vdev, &s->vring);
 | 
					        vring_disable_notification(s->vdev, &s->vring);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (;;) {
 | 
					        for (;;) {
 | 
				
			||||||
            head = vring_pop(s->vdev, &s->vring, iov, end, &out_num, &in_num);
 | 
					            ret = vring_pop(s->vdev, &s->vring, &elem);
 | 
				
			||||||
            if (head < 0) {
 | 
					            if (ret < 0) {
 | 
				
			||||||
 | 
					                assert(elem == NULL);
 | 
				
			||||||
                break; /* no more requests */
 | 
					                break; /* no more requests */
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            trace_virtio_blk_data_plane_process_request(s, out_num, in_num,
 | 
					            trace_virtio_blk_data_plane_process_request(s, elem->out_num,
 | 
				
			||||||
                                                        head);
 | 
					                                                        elem->in_num, elem->index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (process_request(&s->ioqueue, iov, out_num, in_num, head) < 0) {
 | 
					            if (process_request(&s->ioqueue, elem) < 0) {
 | 
				
			||||||
                vring_set_broken(&s->vring);
 | 
					                vring_set_broken(&s->vring);
 | 
				
			||||||
 | 
					                vring_free_element(elem);
 | 
				
			||||||
                ret = -EFAULT;
 | 
					                ret = -EFAULT;
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            iov += out_num + in_num;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (likely(head == -EAGAIN)) { /* vring emptied */
 | 
					        if (likely(ret == -EAGAIN)) { /* vring emptied */
 | 
				
			||||||
            /* Re-enable guest->host notifies and stop processing the vring.
 | 
					            /* Re-enable guest->host notifies and stop processing the vring.
 | 
				
			||||||
             * But if the guest has snuck in more descriptors, keep processing.
 | 
					             * But if the guest has snuck in more descriptors, keep processing.
 | 
				
			||||||
             */
 | 
					             */
 | 
				
			||||||
            if (vring_enable_notification(s->vdev, &s->vring)) {
 | 
					            if (vring_enable_notification(s->vdev, &s->vring)) {
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else { /* head == -ENOBUFS or fatal error, iovecs[] is depleted */
 | 
					        } else { /* ret == -ENOBUFS or fatal error, iovecs[] is depleted */
 | 
				
			||||||
            /* Since there are no iovecs[] left, stop processing for now.  Do
 | 
					            /* Since there are no iovecs[] left, stop processing for now.  Do
 | 
				
			||||||
             * not re-enable guest->host notifies since the I/O completion
 | 
					             * not re-enable guest->host notifies since the I/O completion
 | 
				
			||||||
             * handler knows to check for more vring descriptors anyway.
 | 
					             * handler knows to check for more vring descriptors anyway.
 | 
				
			||||||
 | 
				
			|||||||
@ -111,29 +111,32 @@ bool vring_should_notify(VirtIODevice *vdev, Vring *vring)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int get_desc(Vring *vring,
 | 
					static int get_desc(Vring *vring, VirtQueueElement *elem,
 | 
				
			||||||
                    struct iovec iov[], struct iovec *iov_end,
 | 
					 | 
				
			||||||
                    unsigned int *out_num, unsigned int *in_num,
 | 
					 | 
				
			||||||
                    struct vring_desc *desc)
 | 
					                    struct vring_desc *desc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    unsigned *num;
 | 
					    unsigned *num;
 | 
				
			||||||
 | 
					    struct iovec *iov;
 | 
				
			||||||
 | 
					    hwaddr *addr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (desc->flags & VRING_DESC_F_WRITE) {
 | 
					    if (desc->flags & VRING_DESC_F_WRITE) {
 | 
				
			||||||
        num = in_num;
 | 
					        num = &elem->in_num;
 | 
				
			||||||
 | 
					        iov = &elem->in_sg[*num];
 | 
				
			||||||
 | 
					        addr = &elem->in_addr[*num];
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        num = out_num;
 | 
					        num = &elem->out_num;
 | 
				
			||||||
 | 
					        iov = &elem->out_sg[*num];
 | 
				
			||||||
 | 
					        addr = &elem->out_addr[*num];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* If it's an output descriptor, they're all supposed
 | 
					        /* If it's an output descriptor, they're all supposed
 | 
				
			||||||
         * to come before any input descriptors. */
 | 
					         * to come before any input descriptors. */
 | 
				
			||||||
        if (unlikely(*in_num)) {
 | 
					        if (unlikely(elem->in_num)) {
 | 
				
			||||||
            error_report("Descriptor has out after in");
 | 
					            error_report("Descriptor has out after in");
 | 
				
			||||||
            return -EFAULT;
 | 
					            return -EFAULT;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Stop for now if there are not enough iovecs available. */
 | 
					    /* Stop for now if there are not enough iovecs available. */
 | 
				
			||||||
    iov += *in_num + *out_num;
 | 
					    if (*num >= VIRTQUEUE_MAX_SIZE) {
 | 
				
			||||||
    if (iov >= iov_end) {
 | 
					 | 
				
			||||||
        return -ENOBUFS;
 | 
					        return -ENOBUFS;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -147,14 +150,13 @@ static int get_desc(Vring *vring,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    iov->iov_len = desc->len;
 | 
					    iov->iov_len = desc->len;
 | 
				
			||||||
 | 
					    *addr = desc->addr;
 | 
				
			||||||
    *num += 1;
 | 
					    *num += 1;
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* This is stolen from linux/drivers/vhost/vhost.c. */
 | 
					/* This is stolen from linux/drivers/vhost/vhost.c. */
 | 
				
			||||||
static int get_indirect(Vring *vring,
 | 
					static int get_indirect(Vring *vring, VirtQueueElement *elem,
 | 
				
			||||||
                        struct iovec iov[], struct iovec *iov_end,
 | 
					 | 
				
			||||||
                        unsigned int *out_num, unsigned int *in_num,
 | 
					 | 
				
			||||||
                        struct vring_desc *indirect)
 | 
					                        struct vring_desc *indirect)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    struct vring_desc desc;
 | 
					    struct vring_desc desc;
 | 
				
			||||||
@ -212,7 +214,7 @@ static int get_indirect(Vring *vring,
 | 
				
			|||||||
            return -EFAULT;
 | 
					            return -EFAULT;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ret = get_desc(vring, iov, iov_end, out_num, in_num, &desc);
 | 
					        ret = get_desc(vring, elem, &desc);
 | 
				
			||||||
        if (ret < 0) {
 | 
					        if (ret < 0) {
 | 
				
			||||||
            vring->broken |= (ret == -EFAULT);
 | 
					            vring->broken |= (ret == -EFAULT);
 | 
				
			||||||
            return ret;
 | 
					            return ret;
 | 
				
			||||||
@ -222,6 +224,11 @@ static int get_indirect(Vring *vring,
 | 
				
			|||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void vring_free_element(VirtQueueElement *elem)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    g_slice_free(VirtQueueElement, elem);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* This looks in the virtqueue and for the first available buffer, and converts
 | 
					/* This looks in the virtqueue and for the first available buffer, and converts
 | 
				
			||||||
 * it to an iovec for convenient access.  Since descriptors consist of some
 | 
					 * it to an iovec for convenient access.  Since descriptors consist of some
 | 
				
			||||||
 * number of output then some number of input descriptors, it's actually two
 | 
					 * number of output then some number of input descriptors, it's actually two
 | 
				
			||||||
@ -234,12 +241,12 @@ static int get_indirect(Vring *vring,
 | 
				
			|||||||
 * Stolen from linux/drivers/vhost/vhost.c.
 | 
					 * Stolen from linux/drivers/vhost/vhost.c.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int vring_pop(VirtIODevice *vdev, Vring *vring,
 | 
					int vring_pop(VirtIODevice *vdev, Vring *vring,
 | 
				
			||||||
              struct iovec iov[], struct iovec *iov_end,
 | 
					              VirtQueueElement **p_elem)
 | 
				
			||||||
              unsigned int *out_num, unsigned int *in_num)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    struct vring_desc desc;
 | 
					    struct vring_desc desc;
 | 
				
			||||||
    unsigned int i, head, found = 0, num = vring->vr.num;
 | 
					    unsigned int i, head, found = 0, num = vring->vr.num;
 | 
				
			||||||
    uint16_t avail_idx, last_avail_idx;
 | 
					    uint16_t avail_idx, last_avail_idx;
 | 
				
			||||||
 | 
					    VirtQueueElement *elem = NULL;
 | 
				
			||||||
    int ret;
 | 
					    int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* If there was a fatal error then refuse operation */
 | 
					    /* If there was a fatal error then refuse operation */
 | 
				
			||||||
@ -273,6 +280,10 @@ int vring_pop(VirtIODevice *vdev, Vring *vring,
 | 
				
			|||||||
     * the index we've seen. */
 | 
					     * the index we've seen. */
 | 
				
			||||||
    head = vring->vr.avail->ring[last_avail_idx % num];
 | 
					    head = vring->vr.avail->ring[last_avail_idx % num];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    elem = g_slice_new(VirtQueueElement);
 | 
				
			||||||
 | 
					    elem->index = head;
 | 
				
			||||||
 | 
					    elem->in_num = elem->out_num = 0;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    /* If their number is silly, that's an error. */
 | 
					    /* If their number is silly, that's an error. */
 | 
				
			||||||
    if (unlikely(head >= num)) {
 | 
					    if (unlikely(head >= num)) {
 | 
				
			||||||
        error_report("Guest says index %u > %u is available", head, num);
 | 
					        error_report("Guest says index %u > %u is available", head, num);
 | 
				
			||||||
@ -284,9 +295,6 @@ int vring_pop(VirtIODevice *vdev, Vring *vring,
 | 
				
			|||||||
        vring_avail_event(&vring->vr) = vring->vr.avail->idx;
 | 
					        vring_avail_event(&vring->vr) = vring->vr.avail->idx;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* When we start there are none of either input nor output. */
 | 
					 | 
				
			||||||
    *out_num = *in_num = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    i = head;
 | 
					    i = head;
 | 
				
			||||||
    do {
 | 
					    do {
 | 
				
			||||||
        if (unlikely(i >= num)) {
 | 
					        if (unlikely(i >= num)) {
 | 
				
			||||||
@ -306,14 +314,14 @@ int vring_pop(VirtIODevice *vdev, Vring *vring,
 | 
				
			|||||||
        barrier();
 | 
					        barrier();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (desc.flags & VRING_DESC_F_INDIRECT) {
 | 
					        if (desc.flags & VRING_DESC_F_INDIRECT) {
 | 
				
			||||||
            int ret = get_indirect(vring, iov, iov_end, out_num, in_num, &desc);
 | 
					            int ret = get_indirect(vring, elem, &desc);
 | 
				
			||||||
            if (ret < 0) {
 | 
					            if (ret < 0) {
 | 
				
			||||||
                goto out;
 | 
					                goto out;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ret = get_desc(vring, iov, iov_end, out_num, in_num, &desc);
 | 
					        ret = get_desc(vring, elem, &desc);
 | 
				
			||||||
        if (ret < 0) {
 | 
					        if (ret < 0) {
 | 
				
			||||||
            goto out;
 | 
					            goto out;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -323,6 +331,7 @@ int vring_pop(VirtIODevice *vdev, Vring *vring,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /* On success, increment avail index. */
 | 
					    /* On success, increment avail index. */
 | 
				
			||||||
    vring->last_avail_idx++;
 | 
					    vring->last_avail_idx++;
 | 
				
			||||||
 | 
					    *p_elem = elem;
 | 
				
			||||||
    return head;
 | 
					    return head;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
@ -330,6 +339,10 @@ out:
 | 
				
			|||||||
    if (ret == -EFAULT) {
 | 
					    if (ret == -EFAULT) {
 | 
				
			||||||
        vring->broken = true;
 | 
					        vring->broken = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (elem) {
 | 
				
			||||||
 | 
					        vring_free_element(elem);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    *p_elem = NULL;
 | 
				
			||||||
    return ret;
 | 
					    return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -337,11 +350,14 @@ out:
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * Stolen from linux/drivers/vhost/vhost.c.
 | 
					 * Stolen from linux/drivers/vhost/vhost.c.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void vring_push(Vring *vring, unsigned int head, int len)
 | 
					void vring_push(Vring *vring, VirtQueueElement *elem, int len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    struct vring_used_elem *used;
 | 
					    struct vring_used_elem *used;
 | 
				
			||||||
 | 
					    unsigned int head = elem->index;
 | 
				
			||||||
    uint16_t new;
 | 
					    uint16_t new;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vring_free_element(elem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Don't touch vring if a fatal error occurred */
 | 
					    /* Don't touch vring if a fatal error occurred */
 | 
				
			||||||
    if (vring->broken) {
 | 
					    if (vring->broken) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
 | 
				
			|||||||
@ -54,9 +54,8 @@ void vring_teardown(Vring *vring, VirtIODevice *vdev, int n);
 | 
				
			|||||||
void vring_disable_notification(VirtIODevice *vdev, Vring *vring);
 | 
					void vring_disable_notification(VirtIODevice *vdev, Vring *vring);
 | 
				
			||||||
bool vring_enable_notification(VirtIODevice *vdev, Vring *vring);
 | 
					bool vring_enable_notification(VirtIODevice *vdev, Vring *vring);
 | 
				
			||||||
bool vring_should_notify(VirtIODevice *vdev, Vring *vring);
 | 
					bool vring_should_notify(VirtIODevice *vdev, Vring *vring);
 | 
				
			||||||
int vring_pop(VirtIODevice *vdev, Vring *vring,
 | 
					int vring_pop(VirtIODevice *vdev, Vring *vring, VirtQueueElement **elem);
 | 
				
			||||||
              struct iovec iov[], struct iovec *iov_end,
 | 
					void vring_push(Vring *vring, VirtQueueElement *elem, int len);
 | 
				
			||||||
              unsigned int *out_num, unsigned int *in_num);
 | 
					void vring_free_element(VirtQueueElement *elem);
 | 
				
			||||||
void vring_push(Vring *vring, unsigned int head, int len);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* VRING_H */
 | 
					#endif /* VRING_H */
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user