On Thu, Oct 17, 2024 at 5:42 PM Akihiko Odaki <akihiko.od...@daynix.com> wrote:
>
> On 2024/10/17 18:17, Laurent Vivier wrote:
> > On 17/10/2024 11:07, Akihiko Odaki wrote:
> >> On 2024/10/17 16:32, Laurent Vivier wrote:
> >>> On 17/10/2024 08:59, Jason Wang wrote:
> >>>> On Mon, Oct 14, 2024 at 11:16 PM Laurent Vivier <lviv...@redhat.com>
> >>>> wrote:
> >>>>>
> >>>>> On 14/10/2024 10:30, Laurent Vivier wrote:
> >>>>>> Hi Akihiko,
> >>>>>>
> >>>>>> On 04/06/2024 09:37, Jason Wang wrote:
> >>>>>>> From: Akihiko Odaki <akihiko.od...@daynix.com>
> >>>>>>>
> >>>>>>> Multiqueue usage is not negotiated yet when realizing. If more than
> >>>>>>> one queue is added and the guest never requests to enable
> >>>>>>> multiqueue,
> >>>>>>> the extra queues will not be deleted when unrealizing and leak.
> >>>>>>>
> >>>>>>> Fixes: f9d6dbf0bf6e ("virtio-net: remove virtio queues if the
> >>>>>>> guest doesn't support
> >>>>>>> multiqueue")
> >>>>>>> Signed-off-by: Akihiko Odaki <akihiko.od...@daynix.com>
> >>>>>>> Signed-off-by: Jason Wang <jasow...@redhat.com>
> >>>>>>> ---
> >>>>>>>    hw/net/virtio-net.c | 4 +---
> >>>>>>>    1 file changed, 1 insertion(+), 3 deletions(-)
> >>>>>>>
> >>>>>>> diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
> >>>>>>> index 3cee2ef3ac..a8db8bfd9c 100644
> >>>>>>> --- a/hw/net/virtio-net.c
> >>>>>>> +++ b/hw/net/virtio-net.c
> >>>>>>> @@ -3743,9 +3743,7 @@ static void
> >>>>>>> virtio_net_device_realize(DeviceState *dev, Error **errp)
> >>>>>>>        n->net_conf.tx_queue_size =
> >>>>>>> MIN(virtio_net_max_tx_queue_size(n),
> >>>>>>>                                        n->net_conf.tx_queue_size);
> >>>>>>> -    for (i = 0; i < n->max_queue_pairs; i++) {
> >>>>>>> -        virtio_net_add_queue(n, i);
> >>>>>>> -    }
> >>>>>>> +    virtio_net_add_queue(n, 0);
> >>>>>>>        n->ctrl_vq = virtio_add_queue(vdev, 64,
> >>>>>>> virtio_net_handle_ctrl);
> >>>>>>>        qemu_macaddr_default_if_unset(&n->nic_conf.macaddr);
> >>>>>>
> >>>>>> This change breaks virtio net migration when multiqueue is enabled.
> >>>>>>
> >>>>>> I think this is because virtqueues are half initialized after
> >>>>>> migration : they are
> >>>>>> initialized on guest side (kernel is using them) but not on QEMU
> >>>>>> side (realized has only
> >>>>>> initialized one). After migration, they are not initialized by the
> >>>>>> call to
> >>>>>> virtio_net_set_multiqueue() from virtio_net_set_features() because
> >>>>>> virtio_get_num_queues()
> >>>>>> reports already n->max_queue_pairs as this value is coming from
> >>>>>> the source guest memory.
> >>>>>>
> >>>>>> I don't think we have a way to half-initialize a virtqueue (to
> >>>>>> initialize them only on
> >>>>>> QEMU side as they are already initialized on kernel side).
> >>>>>>
> >>>>>> I think this change should be reverted to fix the migration issue.
> >>>>>>
> >>>>>
> >>>>> Moreover, if I look in the code of virtio_load() and
> >>>>> virtio_add_queue() we can guess it's
> >>>>> not correct to migrate a virtqueue that is not initialized on the
> >>>>> destination side because
> >>>>> fields like 'vdev->vq[i].handle_output' or 'vdev->vq[i].used_elems'
> >>>>> cannot be initialized
> >>>>> by virtio_load() and neither by virtio_add_queue() after
> >>>>> virtio_load() as fields like
> >>>>> 'vring.num' are already initialized by virtio_load().
> >>>>>
> >>>>> For instance, in virtio_load() we set:
> >>>>>
> >>>>>       for (i = 0; i < num; i++) {
> >>>>>           vdev->vq[i].vring.num = qemu_get_be32(f);
> >>>>>
> >>>>> and in virtio_add_queue() we search for the firt available queue to
> >>>>> add with:
> >>>>>
> >>>>>       for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
> >>>>>           if (vdev->vq[i].vring.num == 0)
> >>>>>               break;
> >>>>>       }
> >>>>>
> >>>>> So virtio_add_queue() cannot be used to set:
> >>>>>
> >>>>>       vdev->vq[i].handle_output = handle_output;
> >>>>>       vdev->vq[i].used_elems = g_new0(VirtQueueElement, queue_size);
> >>>>>
> >>>>> Moreover it would overwrite fields already set by virtio_load():
> >>>>>
> >>>>>       vdev->vq[i].vring.num = queue_size;
> >>>>>       vdev->vq[i].vring.align = VIRTIO_PCI_VRING_ALIGN;
> >>>>>
> >>>>> It also explains why virtio_net_change_num_queue_pairs()
> >>>>> (indirectly called by
> >>>>> virtio_net_set_features()) doesn't update the queue pair numbers:
> >>>>> vring.num is already set
> >>>>> so it thinks there is no more queues to add.
> >>>>>
> >>>>> Thanks,
> >>>>> LAurent
> >>>>>
> >>>>
> >>>> I agree.
> >>>>
> >>>> Laurent, would you like to send a patch to revert this?
> >>>>
> >>>
> >>> Yes. I will also try to fix the leak in unrealize that the patch
> >>> wanted to fix initially.
> >>
> >> I wrote a fix so I will submit it once internal testing is done. You
> >> can see the change at:
> >> https://gitlab.com/akihiko.odaki/qemu-kvm/-/
> >> commit/22161221aa2d2031d7ad1be7701852083aa9109a
> >
> > It works fine for me but I don't know if it's a good idea to add queues
> > while the state is loading.
>
> I couldn't come up with other options. The problem is that the number of
> queues added during realization does not match with the loaded state. We
> need to add queues after knowing the negotiated feature set and before
> loading the queue states to fix this problem.
>
> Reverting will add queues that are used when the multiqueue feature is
> negotiated so it will fix migration for such cases, but will also break
> the other cases (i.e., the multiqueue feature is not negotiated) as it
> adds too many queues.
>
> Regards,
> Akihiko Odaki

I wonder if the following is much more simpler:

1) introducing booleans whether the queue has been deleted
2) in unrelize, deleted only the queue that has not been deleted

?

Thanks

>
> >
> > Jason, let me know which solution you prefer (revert or pre_load_queues
> > helper).
> >
> > CC'ing MST
> >
> > Thanks,
> > Laurent
> >
>


Reply via email to