* [PATCH] virtio_console: fix race between hvc put_chars and virtqueue teardown on freeze
@ 2026-05-15 22:52 Sungho Bae
0 siblings, 0 replies; only message in thread
From: Sungho Bae @ 2026-05-15 22:52 UTC (permalink / raw)
To: amit, arnd, gregkh; +Cc: virtualization, linux-kernel, Sungho Bae
From: Sungho Bae <baver.bae@lge.com>
With no_console_suspend enabled, hvc console output can continue while
virtio_console is freezing. In that window, put_chars can still enqueue
buffers to the output virtqueue while virtcons_freeze is tearing queues
down.
This races with virtqueue_detach_unused_buf_split, which expects all
descriptors to be reclaimed during teardown and can hit:
BUG_ON(vq->vq.num_free != vq->split.vring.num)
Root cause is concurrent virtqueue_add_outbuf activity from the console
TX path during freeze.
Add a pm_freezing flag in ports_device and set it at the beginning of
virtcons_freeze. Check the flag in put_chars and __send_to_port to drop
output while freeze is in progress, preventing new buffer enqueue before
vq teardown. Clear the flag on restore only after all port->out_vq
pointers have been reassigned to the newly allocated virtqueues, which
prevents a use-after-free window where TX paths could dereference
already-freed virtqueues.
Place the pm_freezing check inside outvq_lock in __send_to_port so that
it serializes with remove_port_data, which also takes outvq_lock during
freeze. This provides a formal guarantee: once remove_port_data returns
for a given port, no concurrent __send_to_port call can still be adding
buffers to the vq, eliminating the race window with remove_vqs.
Use smp_store_release/smp_load_acquire for the flag to guarantee correct
cross-CPU visibility on weakly ordered architectures.
Signed-off-by: Sungho Bae <baver.bae@lge.com>
---
drivers/char/virtio_console.c | 46 +++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 9a33217c68d9..0fbe40a7f1e7 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -157,6 +157,12 @@ struct ports_device {
/* Major number for this device. Ports will be created as minors. */
int chr_major;
+
+ /*
+ * Set to true during PM freeze to block TX paths that may race
+ * with virtqueue teardown (e.g. hvc put_chars with no_console_suspend).
+ */
+ bool pm_freezing;
};
struct port_stats {
@@ -606,6 +612,20 @@ static ssize_t __send_to_port(struct port *port, struct scatterlist *sg,
spin_lock_irqsave(&port->outvq_lock, flags);
+ /*
+ * Check freeze flag under the lock so that the flag check and
+ * virtqueue_add_outbuf() are atomic with respect to
+ * remove_port_data() which also takes outvq_lock. This
+ * guarantees that once remove_port_data() returns, no new
+ * buffers can be added before remove_vqs() tears down the vq.
+ * Pairs with smp_store_release() in virtcons_freeze/restore.
+ */
+ if (!port->portdev ||
+ smp_load_acquire(&port->portdev->pm_freezing)) { /* pairs with freeze/restore */
+ in_count = 0;
+ goto done;
+ }
+
reclaim_consumed_buffers(port);
err = virtqueue_add_outbuf(out_vq, sg, nents, data, GFP_ATOMIC);
@@ -1115,6 +1135,17 @@ static ssize_t put_chars(u32 vtermno, const u8 *buf, size_t count)
if (!port)
return -EPIPE;
+ /*
+ * Silently drop output during freeze: the console is still active
+ * with no_console_suspend but the virtqueues are being torn down.
+ * This early check avoids a pointless GFP_ATOMIC allocation;
+ * __send_to_port() rechecks under outvq_lock for correctness.
+ * Pairs with smp_store_release() in virtcons_freeze/restore.
+ */
+ if (!port->portdev ||
+ smp_load_acquire(&port->portdev->pm_freezing)) /* pairs with freeze/restore */
+ return count;
+
data = kmemdup(buf, count, GFP_ATOMIC);
if (!data)
return -ENOMEM;
@@ -1972,6 +2003,7 @@ static int virtcons_probe(struct virtio_device *vdev)
/* Attach this portdev to this virtio_device, and vice-versa. */
portdev->vdev = vdev;
vdev->priv = portdev;
+ portdev->pm_freezing = false;
portdev->chr_major = register_chrdev(0, "virtio-portsdev",
&portdev_fops);
@@ -2092,6 +2124,13 @@ static int virtcons_freeze(struct virtio_device *vdev)
portdev = vdev->priv;
+ /*
+ * Block TX paths (put_chars, __send_to_port) before resetting the
+ * device and tearing down virtqueues. This prevents races with
+ * hvc console writes that remain active under no_console_suspend.
+ */
+ smp_store_release(&portdev->pm_freezing, true);
+
virtio_reset_device(vdev);
if (use_multiport(portdev))
@@ -2153,6 +2192,13 @@ static int virtcons_restore(struct virtio_device *vdev)
if (port->guest_connected)
send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
}
+
+ /*
+ * Allow TX paths only after all port->out_vq pointers have
+ * been reassigned to the newly allocated virtqueues.
+ */
+ smp_store_release(&portdev->pm_freezing, false);
+
return 0;
}
#endif
--
2.34.1
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-05-15 22:56 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-15 22:52 [PATCH] virtio_console: fix race between hvc put_chars and virtqueue teardown on freeze Sungho Bae
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.