diff options
| author | Michael S. Tsirkin <mst@redhat.com> | 2026-07-05 05:38:57 -0400 |
|---|---|---|
| committer | Michael S. Tsirkin <mst@redhat.com> | 2026-08-03 23:08:15 -0400 |
| commit | 60039faf8135ff05714a2014e54597ebfe07340f (patch) | |
| tree | 8196f74a6e9d8a3b7c35907d76211d95bcd92637 | |
| parent | 42bc45df5905e2b7dccb72adaf7730f66cfbe03f (diff) | |
| download | linux-next-60039faf8135ff05714a2014e54597ebfe07340f.tar.gz linux-next-60039faf8135ff05714a2014e54597ebfe07340f.zip | |
virtio_balloon: prime stats vq after virtio_device_ready()
The virtio spec requires the driver not to kick the device before
DRIVER_OK is set. init_vqs() primes the stats virtqueue with a buffer
and kicks the device before virtio_device_ready() is called in
virtballoon_probe(), violating this requirement.
Further, if the device responds to the early kick by processing the
buffer before DRIVER_OK, stats_request() fires and queues
update_balloon_stats_work. Should probe then fail and free vb, the work
runs against freed memory.
To fix, move buffer setup to after DRIVER_OK. Be careful to
disable update_balloon_stats_work while this is going on,
to make sure it does not race with the setup.
setup_vqs() warns but does not fail probe or restore if
virtqueue_add_outbuf() fails; the call never actually fails in these
contexts since the queue is freshly initialized and empty.
Testing: tested that stats still work after the change.
Fixes: 9564e138b1f6 ("virtio: Add memory statistics reporting to the balloon driver (V4)")
Reported-by: Sashiko:gemini-3.1-pro-preview
Cc: David Hildenbrand <david@kernel.org>
Assisted-by: Claude:claude-sonnet-4-6
Message-ID: <e44dbd5010b20983f16ab6ec0512dba3190adcaf.1783346070.git.mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
| -rw-r--r-- | drivers/virtio/virtio_balloon.c | 51 |
1 files changed, 33 insertions, 18 deletions
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 581ac799d974..7c5ef4e5c879 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -611,25 +611,9 @@ static int init_vqs(struct virtio_balloon *vb) vb->inflate_vq = vqs[VIRTIO_BALLOON_VQ_INFLATE]; vb->deflate_vq = vqs[VIRTIO_BALLOON_VQ_DEFLATE]; if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_STATS_VQ)) { - struct scatterlist sg; - unsigned int num_stats; vb->stats_vq = vqs[VIRTIO_BALLOON_VQ_STATS]; - - /* - * Prime this virtqueue with one buffer so the hypervisor can - * use it to signal us later (it can't be broken yet!). - */ - num_stats = update_balloon_stats(vb); - - sg_init_one(&sg, vb->stats, sizeof(vb->stats[0]) * num_stats); - err = virtqueue_add_outbuf(vb->stats_vq, &sg, 1, vb, - GFP_KERNEL); - if (err) { - dev_warn(&vb->vdev->dev, "%s: add stat_vq failed\n", - __func__); - return err; - } - virtqueue_kick(vb->stats_vq); + /* Prevent update_balloon_stats_work from accessing the stats vq. */ + disable_work(&vb->update_balloon_stats_work); } if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT)) @@ -916,6 +900,33 @@ static int virtio_balloon_register_shrinker(struct virtio_balloon *vb) return 0; } +static void setup_vqs(struct virtio_balloon *vb) +{ + struct scatterlist sg; + unsigned int num_stats; + bool ret; + + if (!virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_STATS_VQ)) + return; + + /* + * Prime this virtqueue with one buffer so the hypervisor can + * use it to signal us later (it can't be broken yet!). + */ + num_stats = update_balloon_stats(vb); + sg_init_one(&sg, vb->stats, sizeof(vb->stats[0]) * num_stats); + if (virtqueue_add_outbuf(vb->stats_vq, &sg, 1, vb, GFP_KERNEL)) { + dev_warn(&vb->vdev->dev, "%s: add stat_vq failed\n", __func__); + return; + } + virtqueue_kick(vb->stats_vq); + + ret = enable_and_queue_work(system_freezable_wq, + &vb->update_balloon_stats_work); + /* Make sure we balanced enable/disable, or we won't report stats. */ + WARN_ON_ONCE(!ret); +} + static int virtballoon_probe(struct virtio_device *vdev) { struct virtio_balloon *vb; @@ -1056,6 +1067,8 @@ static int virtballoon_probe(struct virtio_device *vdev) virtio_device_ready(vdev); + setup_vqs(vb); + if (towards_target(vb)) virtballoon_changed(vdev); return 0; @@ -1145,6 +1158,8 @@ static int virtballoon_restore(struct virtio_device *vdev) virtio_device_ready(vdev); + setup_vqs(vb); + if (towards_target(vb)) virtballoon_changed(vdev); update_balloon_size(vb); |
