On Mon, Oct 26, 2026 at 11:09:59AM -0400, Michael S. Tsirkin wrote:
> The virtio_balloon driver reports a scatter-gather list of free pages
> to the VMM so it may discard unused pages.
> 
> For an arm64 kernel with 64 KiB pages, the default reporting and
> allocation orders allow entries ranging from 2 MiB to 512 MiB. If the
> report contains a sufficient number of large entries, we will end up
> with a descriptor chain that represents over 4 GiB of memory. This
> violates virtio spec, which says:
>       Drivers MUST NOT add a descriptor chain longer than $2^{32}$ bytes in 
> total;
> 
> Specifically, submitting $2^{32}$ or more breaks Rust-VMM.
> 
> Submit each reported page as a separate virtqueue buffer instead of one
> buffer with an sg list of all pages.  This also avoids indirect
> descriptor allocation (kmalloc in the reporting path).
> 
> To avoid multiple buffers causing interrupt storms, keep callbacks
> disabled while we wait for an event.
> 
> Note: if the virtqueue is broken, wait_event on
> virtqueue_get_buf hangs.  This is a pre-existing issue:
> the old single-buffer code had the same hang.
> 
> Reported-by: Ruben Hakobyan <[email protected]>
> Signed-off-by: Michael S. Tsirkin <[email protected]>

brown paperbag post,forgot to commit.

> ---
> 
> Lightly tested. Ruben, could you pls help test?
> 
>  drivers/virtio/virtio_balloon.c | 48 ++++++++++++++++++++++++---------
>  1 file changed, 35 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
> index f6c2dff33f8a..363519fa890d 100644
> --- a/drivers/virtio/virtio_balloon.c
> +++ b/drivers/virtio/virtio_balloon.c
> @@ -202,25 +202,47 @@ static int virtballoon_free_page_report(struct 
> page_reporting_dev_info *pr_dev_i
>       struct virtio_balloon *vb =
>               container_of(pr_dev_info, struct virtio_balloon, pr_dev_info);
>       struct virtqueue *vq = vb->reporting_vq;
> -     unsigned int unused, err;
> +     unsigned int i, err = 0;
>  
>       /* We should always be able to add these buffers to an empty queue. */
> -     err = virtqueue_add_inbuf(vq, sg, nents, vb, GFP_NOWAIT);
> +     for (i = 0; i < nents; i++) {
> +             struct scatterlist one;
>  
> -     /*
> -      * In the extremely unlikely case that something has occurred and we
> -      * are able to trigger an error we will simply display a warning
> -      * and exit without actually processing the pages.
> -      */
> -     if (WARN_ON_ONCE(err))
> -             return err;
> +             sg_init_table(&one, 1);
> +             sg_set_page(&one, sg_page(&sg[i]), sg[i].length,
> +                         sg[i].offset);
> +             err = virtqueue_add_inbuf(vq, &one, 1, &sg[i], GFP_NOWAIT);
>  
> -     virtqueue_kick(vq);
> +             /*
> +              * In the extremely unlikely case that something has occurred
> +              * and we are able to trigger an error we will simply display
> +              * a warning and exit without actually processing the pages.
> +              */
> +             if (WARN_ON_ONCE(err)) {
> +                     nents = i;
> +                     break;
> +             }
> +     }
>  
> -     /* When host has read buffer, this completes via balloon_ack */
> -     wait_event(vb->acked, virtqueue_get_buf(vq, &unused));
> +     if (nents) {
> +             virtqueue_kick(vq);
>  
> -     return 0;
> +             /* When host has read buffer, this completes via balloon_ack.
> +              * We report pages as individual buffers, so arm the callback
> +              * to only interrupt once all the queued buffers have been
> +              * used: otherwise we would get one interrupt per page.
> +              */
> +             for (i = 0; i < nents; i++) {
> +                     unsigned int unused;
> +
> +                     virtqueue_enable_cb_delayed_max(vq);
> +                     wait_event(vb->acked,
> +                                virtqueue_get_buf(vq, &unused));
> +                     virtqueue_disable_cb(vq);
> +             }
> +     }
> +
> +     return err;
>  }
>  
>  static void set_page_pfns(struct virtio_balloon *vb,
> -- 
> MST
> 


Reply via email to