diff mbox

[RFC] Libqos virtio API

Message ID 1402995768-4282-1-git-send-email-marc.mari.barcelo@gmail.com
State New
Headers show

Commit Message

Marc Marí June 17, 2014, 9:02 a.m. UTC
This is the draft for the libqos virtio API to create test cases for virtio
devices. I'm looking forward to your comments.

Signed-off-by: Marc Marí <marc.mari.barcelo@gmail.com>
---
 tests/libqos/virtio.h |  148 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 148 insertions(+)
 create mode 100644 tests/libqos/virtio.h

Comments

Stefan Hajnoczi June 18, 2014, 9:02 a.m. UTC | #1
On Tue, Jun 17, 2014 at 11:02:48AM +0200, Marc Marí wrote:
> This is the draft for the libqos virtio API to create test cases for virtio
> devices. I'm looking forward to your comments.
> 
> Signed-off-by: Marc Marí <marc.mari.barcelo@gmail.com>
> ---
>  tests/libqos/virtio.h |  148 +++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 148 insertions(+)
>  create mode 100644 tests/libqos/virtio.h
> 
> diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h
> new file mode 100644
> index 0000000..03f28dc
> --- /dev/null
> +++ b/tests/libqos/virtio.h
> @@ -0,0 +1,148 @@
> +#define VIRTQUEUE_MAX_SIZE 1024
> +
> +struct QVirtioBus
> +{
> +    /* Send a notification IRQ to the device */
> +    void (*notify)(QVirtioDevice *d, uint16_t vector);

Guest->host notification is not an interrupt.  It's a "kick" that's
implemented by writing to a hardware register.

The argument should be the virtqueue number.

> +    
> +    /* Get configuration of the device */
> +    void (*get_config)(QVirtioDevice *d, void *config);
> +    
> +    /* Set configuration of the device */
> +    void (*set_config)(QVirtioDevice *d, void *config);
> +    
> +    /* Get features of the device */
> +    uint32_t (*get_features)(QVirtioDevice *d);
> +    
> +    /* Get status of the device */
> +    uint8_t (*get_status)(QVirtioDevice *d);
> +    
> +    /* Set status of the device  */
> +    void (*set_status)(QVirtioDevice *d, uint8_t val);
> +    
> +    /* Reset the device */
> +    void (*reset)(QVirtioDevice *d);
> +    
> +    /* Check pending IRQs */
> +    uint8_t (*query_isr)(QVirtioDevice *d);

Does this also clear the ISR?

> +    
> +    /* Loop all devices, applying a function to all, or the one specified */
> +    void (*device_foreach)(QVirtioBus *bus, uint16_t device_id, 
> +                void (*func)(QVirtioDevice *dev, void *data), void *data);
> +                
> +    /* Find and return a device */
> +    QVirtioDevice *(*device_find)(uint16_t device_id, int index);
> +};
> +
> +QVirtioBus *qvirtio_pci_init();
> +QVirtioBus *qvirtio_mmio_init();
> +QVirtioBus *qvirtio_ccw_init();

In C you should always give an arguments list.  Use void if there are no
arguments.  In C++ fn() means fn(void) but in C it means legacy K&R
behavior which no one uses due to lack of type-safety.

I don't think it's necessary to provide a function that instantiates a
QVirtioBus.  QVirtioBus is just a struct of function pointers.  It can
be declared static const:

static const QVirtioBus virtio_pci_bus = {
    .reset = virtio_pci_reset,
    ...
};

> +
> +struct QVirtioDevice
> +{
> +    /* Index in all devices of the same type in the bus */
> +    int index;
> +    
> +    /* Device type */
> +    uint16_t device_id;
> +    
> +    /* VQs associated with the device */
> +    QVirtQueue *vq;
> +};
> +
> +/*
> +I'm not sure what implementation of VirtQueue is better, QEMU's or Linux's. I
> +think QEMU implementation is better, because it will be easier to connect the
> +QEMU Virtqueue with the Libaos VirtQueue.
> +
> +The functions to use the VirtQueue are the ones I think necessary in Libqos, but
> +probably there are some missing and some others that are not necessary.
> +*/

Keep in mind that QEMU only cares about popping elements off the
available ring and pushing them onto the used ring.

The Linux guest driver implementation is the opposite.  It pushes
elements onto the available ring and pops them off the used ring.

If in doubt, follow the Linux implementation because you are
implementing the guest side of the interface, not the host side.
diff mbox

Patch

diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h
new file mode 100644
index 0000000..03f28dc
--- /dev/null
+++ b/tests/libqos/virtio.h
@@ -0,0 +1,148 @@ 
+#define VIRTQUEUE_MAX_SIZE 1024
+
+struct QVirtioBus
+{
+    /* Send a notification IRQ to the device */
+    void (*notify)(QVirtioDevice *d, uint16_t vector);
+    
+    /* Get configuration of the device */
+    void (*get_config)(QVirtioDevice *d, void *config);
+    
+    /* Set configuration of the device */
+    void (*set_config)(QVirtioDevice *d, void *config);
+    
+    /* Get features of the device */
+    uint32_t (*get_features)(QVirtioDevice *d);
+    
+    /* Get status of the device */
+    uint8_t (*get_status)(QVirtioDevice *d);
+    
+    /* Set status of the device  */
+    void (*set_status)(QVirtioDevice *d, uint8_t val);
+    
+    /* Reset the device */
+    void (*reset)(QVirtioDevice *d);
+    
+    /* Check pending IRQs */
+    uint8_t (*query_isr)(QVirtioDevice *d);
+    
+    /* Loop all devices, applying a function to all, or the one specified */
+    void (*device_foreach)(QVirtioBus *bus, uint16_t device_id, 
+                void (*func)(QVirtioDevice *dev, void *data), void *data);
+                
+    /* Find and return a device */
+    QVirtioDevice *(*device_find)(uint16_t device_id, int index);
+};
+
+QVirtioBus *qvirtio_pci_init();
+QVirtioBus *qvirtio_mmio_init();
+QVirtioBus *qvirtio_ccw_init();
+
+struct QVirtioDevice
+{
+    /* Index in all devices of the same type in the bus */
+    int index;
+    
+    /* Device type */
+    uint16_t device_id;
+    
+    /* VQs associated with the device */
+    QVirtQueue *vq;
+};
+
+/*
+I'm not sure what implementation of VirtQueue is better, QEMU's or Linux's. I
+think QEMU implementation is better, because it will be easier to connect the
+QEMU Virtqueue with the Libaos VirtQueue.
+
+The functions to use the VirtQueue are the ones I think necessary in Libqos, but
+probably there are some missing and some others that are not necessary.
+*/
+struct QVirtQueue
+{
+    QVRing vring;   
+    uint64_t pa;
+    uint16_t last_avail_idx;
+    uint16_t signalled_used;
+    bool signalled_used_valid;
+    bool notification;
+    uint16_t queue_index;
+    int inuse;
+    uint16_t vector;
+    void (*handle_output)(QVirtioDevice *d, VirtQueue *vq);
+    QVirtioDevice *dev;
+};
+
+typedef struct QVirtQueueElement
+{
+    unsigned int index;
+    unsigned int out_num;
+    unsigned int in_num;
+    uint64_t in_addr[VIRTQUEUE_MAX_SIZE];
+    uint64_t out_addr[VIRTQUEUE_MAX_SIZE];
+    struct iovec in_sg[VIRTQUEUE_MAX_SIZE];
+    struct iovec out_sg[VIRTQUEUE_MAX_SIZE];
+} QVirtQueueElement;
+
+typedef struct QVRingDesc
+{
+    uint64_t addr;
+    uint32_t len;
+    uint16_t flags;
+    uint16_t next;
+} QVRingDesc;
+
+typedef struct QVRingAvail
+{
+    uint16_t flags;
+    uint16_t idx;
+    uint16_t ring[0];
+} VRingAvail;
+
+typedef struct QVRingUsedElem
+{
+    uint32_t id;
+    uint32_t len;
+} QVRingUsedElem;
+
+typedef struct QVRingUsed
+{
+    uint16_t flags;
+    uint16_t idx;
+    VRingUsedElem ring[0];
+} QVRingUsed;
+
+typedef struct QVRing
+{
+    unsigned int num;
+    unsigned int align;
+    uint64_t desc;
+    uint64_t avail;
+    uint64_t used;
+} QVRing;
+
+uint64_t qvring_desc_addr(uint64_t desc_pa, int i);
+uint32_t qvring_desc_len(uint64_t desc_pa, int i);
+uint16_t qvring_desc_flags(uint64_t desc_pa, int i);
+uint16_t qvring_desc_next(uint64_t desc_pa, int i);
+uint16_t qvring_avail_flags(QVirtQueue *vq);
+uint16_t qvring_avail_idx(QVirtQueue *vq);
+uint16_t qvring_avail_ring(QVirtQueue *vq, int i);
+uint16_t qvring_used_event(QVirtQueue *vq);
+void qvring_used_ring_id(QVirtQueue *vq, int i, uint32_t val);
+void qvring_used_ring_len(QVirtQueue *vq, int i, uint32_t val);
+uint16_t qvring_used_idx(QVirtQueue *vq);
+void qvring_used_idx_set(QVirtQueue *vq, uint16_t val);
+void qvring_used_flags_set_bit(QVirtQueue *vq, int mask);
+void qvring_used_flags_unset_bit(QVirtQueue *vq, int mask);
+void qvring_avail_event(QVirtQueue *vq, uint16_t val);
+
+void qvirtqueue_push(QVirtQueue *vq, const QVirtQueueElement *elem, unsigned int len);
+void qvirtqueue_flush(QVirtQueue *vq, unsigned int count);
+void qvirtqueue_fill(QVirtQueue *vq, const VirtQueueElement *elem,  unsigned int len, unsigned int idx);
+void qvirtqueue_pop(QVirtQueue *vq, VirtQueueElement *elem);
+void qvirtqueue_map_sg(struct iovec *sg, uint64_t *addr,
+    size_t num_sg, int is_write);
+void qvirtio_notify(QVirtioDevice *vdev, QVirtQueue *vq);
+int qvirtio_queue_ready(QVirtQueue *vq);
+int qvirtio_queue_empty(QVirtQueue *vq);