Submit each reported page as a separate virtqueue buffer instead
of one buffer with an sg list of all pages. This avoids indirect
descriptor allocation (kmalloc in the reporting path) and gives
per-page used length feedback from the device.

On error, the already-queued pages are kicked and drained
before the error is returned. The caller (page_reporting_drain)
then marks the batch as unreported, which is conservative
but correct.

Signed-off-by: Michael S. Tsirkin <[email protected]>
Assisted-by: Claude:claude-opus-4-6
---
 drivers/virtio/virtio_balloon.c | 36 +++++++++++++++++++++------------
 1 file changed, 23 insertions(+), 13 deletions(-)

diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 7ed024315539..e99ffbbdd2bd 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -202,25 +202,35 @@ 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;
 
        /* 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);
+               if (WARN_ON_ONCE(err)) {
+                       nents = i;
+                       break;
+               }
+       }
 
-       virtqueue_kick(vq);
+       if (nents) {
+               virtqueue_kick(vq);
 
-       /* When host has read buffer, this completes via balloon_ack */
-       wait_event(vb->acked, virtqueue_get_buf(vq, &unused));
+               /* When host has read buffer, this completes via balloon_ack */
+               for (i = 0; i < nents; i++) {
+                       unsigned int unused;
 
-       return 0;
+                       wait_event(vb->acked,
+                                  virtqueue_get_buf(vq, &unused));
+               }
+       }
+
+       return err;
 }
 
 static void set_page_pfns(struct virtio_balloon *vb,
-- 
MST


Reply via email to