diff mbox series

[RFC,v9,12/23] vhost: Add opaque member to SVQElement

Message ID 20220706184008.1649478-13-eperezma@redhat.com
State New
Headers show
Series Net Control VQ support in SVQ | expand

Commit Message

Eugenio Perez Martin July 6, 2022, 6:39 p.m. UTC
When qemu injects buffers to the vdpa device it will be used to maintain
contextual data. If SVQ has no operation, it will be used to maintain
the VirtQueueElement pointer.

Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
---
 hw/virtio/vhost-shadow-virtqueue.h |  3 ++-
 hw/virtio/vhost-shadow-virtqueue.c | 13 +++++++------
 2 files changed, 9 insertions(+), 7 deletions(-)

Comments

Jason Wang July 11, 2022, 9:05 a.m. UTC | #1
在 2022/7/7 02:39, Eugenio Pérez 写道:
> When qemu injects buffers to the vdpa device it will be used to maintain
> contextual data. If SVQ has no operation, it will be used to maintain
> the VirtQueueElement pointer.
>
> Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
> ---
>   hw/virtio/vhost-shadow-virtqueue.h |  3 ++-
>   hw/virtio/vhost-shadow-virtqueue.c | 13 +++++++------
>   2 files changed, 9 insertions(+), 7 deletions(-)
>
> diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
> index 0e434e9fd0..a811f90e01 100644
> --- a/hw/virtio/vhost-shadow-virtqueue.h
> +++ b/hw/virtio/vhost-shadow-virtqueue.h
> @@ -16,7 +16,8 @@
>   #include "hw/virtio/vhost-iova-tree.h"
>   
>   typedef struct SVQElement {
> -    VirtQueueElement *elem;
> +    /* Opaque data */
> +    void *opaque;


So I wonder if we can simply:

1) introduce a opaque to VirtQueueElement
2) store pointers to ring_id_maps

Since

1) VirtQueueElement's member looks general
2) help to reduce the tricky codes like vhost_svq_empty_elem() and 
vhost_svq_empty_elem().

Thanks


>   
>       /* Last descriptor of the chain */
>       uint32_t last_chain_id;
> diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
> index c5e49e51c5..492bb12b5f 100644
> --- a/hw/virtio/vhost-shadow-virtqueue.c
> +++ b/hw/virtio/vhost-shadow-virtqueue.c
> @@ -237,7 +237,7 @@ static uint16_t vhost_svq_last_desc_of_chain(const VhostShadowVirtqueue *svq,
>    */
>   static bool vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
>                             size_t out_num, const struct iovec *in_sg,
> -                          size_t in_num, VirtQueueElement *elem)
> +                          size_t in_num, void *opaque)
>   {
>       SVQElement *svq_elem;
>       unsigned qemu_head;
> @@ -245,13 +245,12 @@ static bool vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
>       bool ok = vhost_svq_add_split(svq, out_sg, out_num, in_sg, in_num,
>                                     &qemu_head);
>       if (unlikely(!ok)) {
> -        g_free(elem);
>           return false;
>       }
>   
>       n = out_num + in_num;
>       svq_elem = &svq->ring_id_maps[qemu_head];
> -    svq_elem->elem = elem;
> +    svq_elem->opaque = opaque;
>       svq_elem->last_chain_id = vhost_svq_last_desc_of_chain(svq, n, qemu_head);
>       return true;
>   }
> @@ -277,6 +276,8 @@ static bool vhost_svq_add_element(VhostShadowVirtqueue *svq,
>                               elem->in_num, elem);
>       if (ok) {
>           vhost_svq_kick(svq);
> +    } else {
> +        g_free(elem);
>       }
>   
>       return ok;
> @@ -392,7 +393,7 @@ static void vhost_svq_disable_notification(VhostShadowVirtqueue *svq)
>   
>   static bool vhost_svq_is_empty_elem(SVQElement elem)
>   {
> -    return elem.elem == NULL;
> +    return elem.opaque == NULL;
>   }
>   
>   static SVQElement vhost_svq_empty_elem(void)
> @@ -483,7 +484,7 @@ static void vhost_svq_flush(VhostShadowVirtqueue *svq,
>                   break;
>               }
>   
> -            elem = g_steal_pointer(&svq_elem.elem);
> +            elem = g_steal_pointer(&svq_elem.opaque);
>               virtqueue_fill(vq, elem, len, i++);
>           }
>   
> @@ -651,7 +652,7 @@ void vhost_svq_stop(VhostShadowVirtqueue *svq)
>   
>       for (unsigned i = 0; i < svq->vring.num; ++i) {
>           g_autofree VirtQueueElement *elem = NULL;
> -        elem = g_steal_pointer(&svq->ring_id_maps[i].elem);
> +        elem = g_steal_pointer(&svq->ring_id_maps[i].opaque);
>           if (elem) {
>               virtqueue_detach_element(svq->vq, elem, 0);
>           }
Eugenio Perez Martin July 11, 2022, 9:56 a.m. UTC | #2
On Mon, Jul 11, 2022 at 11:05 AM Jason Wang <jasowang@redhat.com> wrote:
>
>
> 在 2022/7/7 02:39, Eugenio Pérez 写道:
> > When qemu injects buffers to the vdpa device it will be used to maintain
> > contextual data. If SVQ has no operation, it will be used to maintain
> > the VirtQueueElement pointer.
> >
> > Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
> > ---
> >   hw/virtio/vhost-shadow-virtqueue.h |  3 ++-
> >   hw/virtio/vhost-shadow-virtqueue.c | 13 +++++++------
> >   2 files changed, 9 insertions(+), 7 deletions(-)
> >
> > diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
> > index 0e434e9fd0..a811f90e01 100644
> > --- a/hw/virtio/vhost-shadow-virtqueue.h
> > +++ b/hw/virtio/vhost-shadow-virtqueue.h
> > @@ -16,7 +16,8 @@
> >   #include "hw/virtio/vhost-iova-tree.h"
> >
> >   typedef struct SVQElement {
> > -    VirtQueueElement *elem;
> > +    /* Opaque data */
> > +    void *opaque;
>
>
> So I wonder if we can simply:
>
> 1) introduce a opaque to VirtQueueElement

(answered in other thread, pasting here for completion)

It does not work for messages that are not generated by the guest. For
example, the ones used to restore the device state at live migration
destination.

> 2) store pointers to ring_id_maps
>

I think you mean to keep storing VirtQueueElement at ring_id_maps?
Otherwise, looking for them will not be immediate.

> Since
>
> 1) VirtQueueElement's member looks general

Not general enough :).

> 2) help to reduce the tricky codes like vhost_svq_empty_elem() and
> vhost_svq_empty_elem().
>

I'm ok to change to whatever other method, but to allocate them
individually seems worse to me. Both performance wise and because
error paths are more complicated. Maybe it would be less tricky if I
try to move the use of them less "by value" and more "as pointers"?

Thanks!

> Thanks
>
>
> >
> >       /* Last descriptor of the chain */
> >       uint32_t last_chain_id;
> > diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
> > index c5e49e51c5..492bb12b5f 100644
> > --- a/hw/virtio/vhost-shadow-virtqueue.c
> > +++ b/hw/virtio/vhost-shadow-virtqueue.c
> > @@ -237,7 +237,7 @@ static uint16_t vhost_svq_last_desc_of_chain(const VhostShadowVirtqueue *svq,
> >    */
> >   static bool vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
> >                             size_t out_num, const struct iovec *in_sg,
> > -                          size_t in_num, VirtQueueElement *elem)
> > +                          size_t in_num, void *opaque)
> >   {
> >       SVQElement *svq_elem;
> >       unsigned qemu_head;
> > @@ -245,13 +245,12 @@ static bool vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
> >       bool ok = vhost_svq_add_split(svq, out_sg, out_num, in_sg, in_num,
> >                                     &qemu_head);
> >       if (unlikely(!ok)) {
> > -        g_free(elem);
> >           return false;
> >       }
> >
> >       n = out_num + in_num;
> >       svq_elem = &svq->ring_id_maps[qemu_head];
> > -    svq_elem->elem = elem;
> > +    svq_elem->opaque = opaque;
> >       svq_elem->last_chain_id = vhost_svq_last_desc_of_chain(svq, n, qemu_head);
> >       return true;
> >   }
> > @@ -277,6 +276,8 @@ static bool vhost_svq_add_element(VhostShadowVirtqueue *svq,
> >                               elem->in_num, elem);
> >       if (ok) {
> >           vhost_svq_kick(svq);
> > +    } else {
> > +        g_free(elem);
> >       }
> >
> >       return ok;
> > @@ -392,7 +393,7 @@ static void vhost_svq_disable_notification(VhostShadowVirtqueue *svq)
> >
> >   static bool vhost_svq_is_empty_elem(SVQElement elem)
> >   {
> > -    return elem.elem == NULL;
> > +    return elem.opaque == NULL;
> >   }
> >
> >   static SVQElement vhost_svq_empty_elem(void)
> > @@ -483,7 +484,7 @@ static void vhost_svq_flush(VhostShadowVirtqueue *svq,
> >                   break;
> >               }
> >
> > -            elem = g_steal_pointer(&svq_elem.elem);
> > +            elem = g_steal_pointer(&svq_elem.opaque);
> >               virtqueue_fill(vq, elem, len, i++);
> >           }
> >
> > @@ -651,7 +652,7 @@ void vhost_svq_stop(VhostShadowVirtqueue *svq)
> >
> >       for (unsigned i = 0; i < svq->vring.num; ++i) {
> >           g_autofree VirtQueueElement *elem = NULL;
> > -        elem = g_steal_pointer(&svq->ring_id_maps[i].elem);
> > +        elem = g_steal_pointer(&svq->ring_id_maps[i].opaque);
> >           if (elem) {
> >               virtqueue_detach_element(svq->vq, elem, 0);
> >           }
>
Jason Wang July 12, 2022, 7:53 a.m. UTC | #3
在 2022/7/11 17:56, Eugenio Perez Martin 写道:
> On Mon, Jul 11, 2022 at 11:05 AM Jason Wang <jasowang@redhat.com> wrote:
>>
>> 在 2022/7/7 02:39, Eugenio Pérez 写道:
>>> When qemu injects buffers to the vdpa device it will be used to maintain
>>> contextual data. If SVQ has no operation, it will be used to maintain
>>> the VirtQueueElement pointer.
>>>
>>> Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
>>> ---
>>>    hw/virtio/vhost-shadow-virtqueue.h |  3 ++-
>>>    hw/virtio/vhost-shadow-virtqueue.c | 13 +++++++------
>>>    2 files changed, 9 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
>>> index 0e434e9fd0..a811f90e01 100644
>>> --- a/hw/virtio/vhost-shadow-virtqueue.h
>>> +++ b/hw/virtio/vhost-shadow-virtqueue.h
>>> @@ -16,7 +16,8 @@
>>>    #include "hw/virtio/vhost-iova-tree.h"
>>>
>>>    typedef struct SVQElement {
>>> -    VirtQueueElement *elem;
>>> +    /* Opaque data */
>>> +    void *opaque;
>>
>> So I wonder if we can simply:
>>
>> 1) introduce a opaque to VirtQueueElement
> (answered in other thread, pasting here for completion)
>
> It does not work for messages that are not generated by the guest. For
> example, the ones used to restore the device state at live migration
> destination.


For the ones that requires more metadata, we can store it in elem->opaque?


>
>> 2) store pointers to ring_id_maps
>>
> I think you mean to keep storing VirtQueueElement at ring_id_maps?


Yes and introduce a pointer to metadata in VirtQueueElement


> Otherwise, looking for them will not be immediate.
>
>> Since
>>
>> 1) VirtQueueElement's member looks general
> Not general enough :).
>
>> 2) help to reduce the tricky codes like vhost_svq_empty_elem() and
>> vhost_svq_empty_elem().
>>
> I'm ok to change to whatever other method, but to allocate them
> individually seems worse to me. Both performance wise and because
> error paths are more complicated. Maybe it would be less tricky if I
> try to move the use of them less "by value" and more "as pointers"?


Or let's having a dedicated arrays (like desc_state/desc_extra in 
kernel) instead of trying to reuse ring_id_maps.

Thanks


>
> Thanks!
>
>> Thanks
>>
>>
>>>        /* Last descriptor of the chain */
>>>        uint32_t last_chain_id;
>>> diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
>>> index c5e49e51c5..492bb12b5f 100644
>>> --- a/hw/virtio/vhost-shadow-virtqueue.c
>>> +++ b/hw/virtio/vhost-shadow-virtqueue.c
>>> @@ -237,7 +237,7 @@ static uint16_t vhost_svq_last_desc_of_chain(const VhostShadowVirtqueue *svq,
>>>     */
>>>    static bool vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
>>>                              size_t out_num, const struct iovec *in_sg,
>>> -                          size_t in_num, VirtQueueElement *elem)
>>> +                          size_t in_num, void *opaque)
>>>    {
>>>        SVQElement *svq_elem;
>>>        unsigned qemu_head;
>>> @@ -245,13 +245,12 @@ static bool vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
>>>        bool ok = vhost_svq_add_split(svq, out_sg, out_num, in_sg, in_num,
>>>                                      &qemu_head);
>>>        if (unlikely(!ok)) {
>>> -        g_free(elem);
>>>            return false;
>>>        }
>>>
>>>        n = out_num + in_num;
>>>        svq_elem = &svq->ring_id_maps[qemu_head];
>>> -    svq_elem->elem = elem;
>>> +    svq_elem->opaque = opaque;
>>>        svq_elem->last_chain_id = vhost_svq_last_desc_of_chain(svq, n, qemu_head);
>>>        return true;
>>>    }
>>> @@ -277,6 +276,8 @@ static bool vhost_svq_add_element(VhostShadowVirtqueue *svq,
>>>                                elem->in_num, elem);
>>>        if (ok) {
>>>            vhost_svq_kick(svq);
>>> +    } else {
>>> +        g_free(elem);
>>>        }
>>>
>>>        return ok;
>>> @@ -392,7 +393,7 @@ static void vhost_svq_disable_notification(VhostShadowVirtqueue *svq)
>>>
>>>    static bool vhost_svq_is_empty_elem(SVQElement elem)
>>>    {
>>> -    return elem.elem == NULL;
>>> +    return elem.opaque == NULL;
>>>    }
>>>
>>>    static SVQElement vhost_svq_empty_elem(void)
>>> @@ -483,7 +484,7 @@ static void vhost_svq_flush(VhostShadowVirtqueue *svq,
>>>                    break;
>>>                }
>>>
>>> -            elem = g_steal_pointer(&svq_elem.elem);
>>> +            elem = g_steal_pointer(&svq_elem.opaque);
>>>                virtqueue_fill(vq, elem, len, i++);
>>>            }
>>>
>>> @@ -651,7 +652,7 @@ void vhost_svq_stop(VhostShadowVirtqueue *svq)
>>>
>>>        for (unsigned i = 0; i < svq->vring.num; ++i) {
>>>            g_autofree VirtQueueElement *elem = NULL;
>>> -        elem = g_steal_pointer(&svq->ring_id_maps[i].elem);
>>> +        elem = g_steal_pointer(&svq->ring_id_maps[i].opaque);
>>>            if (elem) {
>>>                virtqueue_detach_element(svq->vq, elem, 0);
>>>            }
Eugenio Perez Martin July 12, 2022, 8:32 a.m. UTC | #4
On Tue, Jul 12, 2022 at 9:53 AM Jason Wang <jasowang@redhat.com> wrote:
>
>
> 在 2022/7/11 17:56, Eugenio Perez Martin 写道:
> > On Mon, Jul 11, 2022 at 11:05 AM Jason Wang <jasowang@redhat.com> wrote:
> >>
> >> 在 2022/7/7 02:39, Eugenio Pérez 写道:
> >>> When qemu injects buffers to the vdpa device it will be used to maintain
> >>> contextual data. If SVQ has no operation, it will be used to maintain
> >>> the VirtQueueElement pointer.
> >>>
> >>> Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
> >>> ---
> >>>    hw/virtio/vhost-shadow-virtqueue.h |  3 ++-
> >>>    hw/virtio/vhost-shadow-virtqueue.c | 13 +++++++------
> >>>    2 files changed, 9 insertions(+), 7 deletions(-)
> >>>
> >>> diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
> >>> index 0e434e9fd0..a811f90e01 100644
> >>> --- a/hw/virtio/vhost-shadow-virtqueue.h
> >>> +++ b/hw/virtio/vhost-shadow-virtqueue.h
> >>> @@ -16,7 +16,8 @@
> >>>    #include "hw/virtio/vhost-iova-tree.h"
> >>>
> >>>    typedef struct SVQElement {
> >>> -    VirtQueueElement *elem;
> >>> +    /* Opaque data */
> >>> +    void *opaque;
> >>
> >> So I wonder if we can simply:
> >>
> >> 1) introduce a opaque to VirtQueueElement
> > (answered in other thread, pasting here for completion)
> >
> > It does not work for messages that are not generated by the guest. For
> > example, the ones used to restore the device state at live migration
> > destination.
>
>
> For the ones that requires more metadata, we can store it in elem->opaque?
>

But there is no VirtQueueElem there. VirtQueueElem is allocated by
virtqueue_pop, but state restoring messages are not received by this
function. If we allocate an artificial one, a lot of members do not
make sense (like in_addr / out_addr), and we should never use them
with virtqueue_push / fill / flush and similar.

>
> >
> >> 2) store pointers to ring_id_maps
> >>
> > I think you mean to keep storing VirtQueueElement at ring_id_maps?
>
>
> Yes and introduce a pointer to metadata in VirtQueueElement
>
>
> > Otherwise, looking for them will not be immediate.
> >
> >> Since
> >>
> >> 1) VirtQueueElement's member looks general
> > Not general enough :).
> >
> >> 2) help to reduce the tricky codes like vhost_svq_empty_elem() and
> >> vhost_svq_empty_elem().
> >>
> > I'm ok to change to whatever other method, but to allocate them
> > individually seems worse to me. Both performance wise and because
> > error paths are more complicated. Maybe it would be less tricky if I
> > try to move the use of them less "by value" and more "as pointers"?
>
>
> Or let's having a dedicated arrays (like desc_state/desc_extra in
> kernel) instead of trying to reuse ring_id_maps.
>

Sure, it looks to me like:
* renaming ring_id_maps to desc_state/desc_extra/something similar,
since now it's used to store more state that only the guest mapping
* Rename "opaque" to "data"
* Forget the wrapper and assume data == NULL is an invalid head /
empty. To me they serve as a doc, but I guess it's fine to use them
directly. The kernel works that way anyway.

Does this look better? It's definitely closer to the kernel so I guess
it's an advantage.

Thanks!
Jason Wang July 12, 2022, 8:43 a.m. UTC | #5
On Tue, Jul 12, 2022 at 4:33 PM Eugenio Perez Martin
<eperezma@redhat.com> wrote:
>
> On Tue, Jul 12, 2022 at 9:53 AM Jason Wang <jasowang@redhat.com> wrote:
> >
> >
> > 在 2022/7/11 17:56, Eugenio Perez Martin 写道:
> > > On Mon, Jul 11, 2022 at 11:05 AM Jason Wang <jasowang@redhat.com> wrote:
> > >>
> > >> 在 2022/7/7 02:39, Eugenio Pérez 写道:
> > >>> When qemu injects buffers to the vdpa device it will be used to maintain
> > >>> contextual data. If SVQ has no operation, it will be used to maintain
> > >>> the VirtQueueElement pointer.
> > >>>
> > >>> Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
> > >>> ---
> > >>>    hw/virtio/vhost-shadow-virtqueue.h |  3 ++-
> > >>>    hw/virtio/vhost-shadow-virtqueue.c | 13 +++++++------
> > >>>    2 files changed, 9 insertions(+), 7 deletions(-)
> > >>>
> > >>> diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
> > >>> index 0e434e9fd0..a811f90e01 100644
> > >>> --- a/hw/virtio/vhost-shadow-virtqueue.h
> > >>> +++ b/hw/virtio/vhost-shadow-virtqueue.h
> > >>> @@ -16,7 +16,8 @@
> > >>>    #include "hw/virtio/vhost-iova-tree.h"
> > >>>
> > >>>    typedef struct SVQElement {
> > >>> -    VirtQueueElement *elem;
> > >>> +    /* Opaque data */
> > >>> +    void *opaque;
> > >>
> > >> So I wonder if we can simply:
> > >>
> > >> 1) introduce a opaque to VirtQueueElement
> > > (answered in other thread, pasting here for completion)
> > >
> > > It does not work for messages that are not generated by the guest. For
> > > example, the ones used to restore the device state at live migration
> > > destination.
> >
> >
> > For the ones that requires more metadata, we can store it in elem->opaque?
> >
>
> But there is no VirtQueueElem there. VirtQueueElem is allocated by
> virtqueue_pop, but state restoring messages are not received by this
> function. If we allocate an artificial one, a lot of members do not
> make sense (like in_addr / out_addr), and we should never use them
> with virtqueue_push / fill / flush and similar.

Ok.

>
> >
> > >
> > >> 2) store pointers to ring_id_maps
> > >>
> > > I think you mean to keep storing VirtQueueElement at ring_id_maps?
> >
> >
> > Yes and introduce a pointer to metadata in VirtQueueElement
> >
> >
> > > Otherwise, looking for them will not be immediate.
> > >
> > >> Since
> > >>
> > >> 1) VirtQueueElement's member looks general
> > > Not general enough :).
> > >
> > >> 2) help to reduce the tricky codes like vhost_svq_empty_elem() and
> > >> vhost_svq_empty_elem().
> > >>
> > > I'm ok to change to whatever other method, but to allocate them
> > > individually seems worse to me. Both performance wise and because
> > > error paths are more complicated. Maybe it would be less tricky if I
> > > try to move the use of them less "by value" and more "as pointers"?
> >
> >
> > Or let's having a dedicated arrays (like desc_state/desc_extra in
> > kernel) instead of trying to reuse ring_id_maps.
> >
>
> Sure, it looks to me like:
> * renaming ring_id_maps to desc_state/desc_extra/something similar,
> since now it's used to store more state that only the guest mapping
> * Rename "opaque" to "data"
> * Forget the wrapper and assume data == NULL is an invalid head /
> empty. To me they serve as a doc, but I guess it's fine to use them
> directly. The kernel works that way anyway.
>
> Does this look better?

Yes.

> It's definitely closer to the kernel so I guess
> it's an advantage.

I think the advantage is that it decouples the dynamic allocated
metadata (VirtQueueElem) out of the static allocated ones.

>
> Thanks!
>
diff mbox series

Patch

diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
index 0e434e9fd0..a811f90e01 100644
--- a/hw/virtio/vhost-shadow-virtqueue.h
+++ b/hw/virtio/vhost-shadow-virtqueue.h
@@ -16,7 +16,8 @@ 
 #include "hw/virtio/vhost-iova-tree.h"
 
 typedef struct SVQElement {
-    VirtQueueElement *elem;
+    /* Opaque data */
+    void *opaque;
 
     /* Last descriptor of the chain */
     uint32_t last_chain_id;
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
index c5e49e51c5..492bb12b5f 100644
--- a/hw/virtio/vhost-shadow-virtqueue.c
+++ b/hw/virtio/vhost-shadow-virtqueue.c
@@ -237,7 +237,7 @@  static uint16_t vhost_svq_last_desc_of_chain(const VhostShadowVirtqueue *svq,
  */
 static bool vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
                           size_t out_num, const struct iovec *in_sg,
-                          size_t in_num, VirtQueueElement *elem)
+                          size_t in_num, void *opaque)
 {
     SVQElement *svq_elem;
     unsigned qemu_head;
@@ -245,13 +245,12 @@  static bool vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
     bool ok = vhost_svq_add_split(svq, out_sg, out_num, in_sg, in_num,
                                   &qemu_head);
     if (unlikely(!ok)) {
-        g_free(elem);
         return false;
     }
 
     n = out_num + in_num;
     svq_elem = &svq->ring_id_maps[qemu_head];
-    svq_elem->elem = elem;
+    svq_elem->opaque = opaque;
     svq_elem->last_chain_id = vhost_svq_last_desc_of_chain(svq, n, qemu_head);
     return true;
 }
@@ -277,6 +276,8 @@  static bool vhost_svq_add_element(VhostShadowVirtqueue *svq,
                             elem->in_num, elem);
     if (ok) {
         vhost_svq_kick(svq);
+    } else {
+        g_free(elem);
     }
 
     return ok;
@@ -392,7 +393,7 @@  static void vhost_svq_disable_notification(VhostShadowVirtqueue *svq)
 
 static bool vhost_svq_is_empty_elem(SVQElement elem)
 {
-    return elem.elem == NULL;
+    return elem.opaque == NULL;
 }
 
 static SVQElement vhost_svq_empty_elem(void)
@@ -483,7 +484,7 @@  static void vhost_svq_flush(VhostShadowVirtqueue *svq,
                 break;
             }
 
-            elem = g_steal_pointer(&svq_elem.elem);
+            elem = g_steal_pointer(&svq_elem.opaque);
             virtqueue_fill(vq, elem, len, i++);
         }
 
@@ -651,7 +652,7 @@  void vhost_svq_stop(VhostShadowVirtqueue *svq)
 
     for (unsigned i = 0; i < svq->vring.num; ++i) {
         g_autofree VirtQueueElement *elem = NULL;
-        elem = g_steal_pointer(&svq->ring_id_maps[i].elem);
+        elem = g_steal_pointer(&svq->ring_id_maps[i].opaque);
         if (elem) {
             virtqueue_detach_element(svq->vq, elem, 0);
         }