summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-08-21 14:23:40 +0100
committerMark Brown <broonie@kernel.org>2026-08-21 14:23:40 +0100
commitebd7776db5abf0d5f37a9e7365430376fee90f50 (patch)
treee719e40f64e6c15131e87722bdc679bbbf4177bc /drivers
parent29dd393351d25ad4362f3eab3a7b9ea241aa67e1 (diff)
parentae864da7d762b4c692e2cdf83cad43110d5d46ee (diff)
downloadlinux-next-ebd7776db5abf0d5f37a9e7365430376fee90f50.tar.gz
linux-next-ebd7776db5abf0d5f37a9e7365430376fee90f50.zip
Merge branch 'linux-next' of https://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost.git
Diffstat (limited to 'drivers')
-rw-r--r--drivers/vhost/vhost.c11
-rw-r--r--drivers/vhost/vsock.c80
-rw-r--r--drivers/virtio/virtio_balloon.c51
3 files changed, 105 insertions, 37 deletions
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 14637cff0bd4..a0c1d54019aa 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -729,6 +729,17 @@ static void vhost_workers_free(struct vhost_dev *dev)
for (i = 0; i < dev->nvqs; i++)
rcu_assign_pointer(dev->vqs[i]->worker, NULL);
+
+ /*
+ * vhost_vq_work_queue() reads vq->worker under rcu_read_lock(), so a
+ * reader that fetched a worker before we cleared the pointers above
+ * may still be queueing work on it. Wait for those readers to
+ * finish, then flush so any work they queued runs (clearing
+ * VHOST_WORK_QUEUED) before the workers are freed.
+ */
+ synchronize_rcu();
+ vhost_dev_flush(dev);
+
/*
* Free the default worker we created and cleanup workers userspace
* created but couldn't clean up (it forgot or crashed).
diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
index 9aaab6bb8061..86f25ff80722 100644
--- a/drivers/vhost/vsock.c
+++ b/drivers/vhost/vsock.c
@@ -61,6 +61,7 @@ struct vhost_vsock {
u32 guest_cid;
bool seqpacket_allow;
+ bool ever_started; /* set on first SET_RUNNING(1); never cleared */
};
static u32 vhost_transport_get_local_cid(void)
@@ -302,17 +303,12 @@ vhost_transport_send_pkt(struct sk_buff *skb, struct net *net)
return -ENODEV;
}
- /* Fast-fail if the guest hasn't enabled the RX vq yet. Queuing the packet
- * and making the caller wait is pointless: even if the guest manages to init
- * within the timeout, it'll immediately reply with RST, because there's no
- * listener on the port yet.
- *
- * vhost_vq_get_backend() without vq->mutex is acceptable here: locking
- * the mutex would be too expensive in this hot path, and we already have
- * all the outcomes covered: if the backend becomes NULL right after the check,
- * vhost_transport_do_send_pkt() will check it under the mutex anyway.
+ /* Fast-fail until the guest first enables the device (SET_RUNNING(1)).
+ * Before that there is no listener, so queuing is pointless.
+ * 'ever_started' is never cleared, so once we're up we keep queuing
+ * across later stop / CPR-pause windows.
*/
- if (unlikely(!data_race(vhost_vq_get_backend(&vsock->vqs[VSOCK_VQ_RX])))) {
+ if (unlikely(!READ_ONCE(vsock->ever_started))) {
rcu_read_unlock();
kfree_skb(skb);
return -EHOSTUNREACH;
@@ -640,11 +636,23 @@ static int vhost_vsock_start(struct vhost_vsock *vsock)
mutex_unlock(&vq->mutex);
}
+ /* Set 'ever_started' flag on the first start; never cleared, so send_pkt
+ * keeps queuing (instead of fast-failing) on later stop / CPR pauses.
+ */
+ WRITE_ONCE(vsock->ever_started, true);
+
/* Some packets may have been queued before the device was started,
* let's kick the send worker to send them.
*/
vhost_vq_work_queue(&vsock->vqs[VSOCK_VQ_RX], &vsock->send_pkt_work);
+ /* The guest may have added TX buffers while the device was stopped
+ * (e.g. across VHOST_RESET_OWNER) and their kicks got consumed by
+ * the NULL-backend window. Re-scan the TX VQ, mirroring the RX
+ * send-worker kick above.
+ */
+ vhost_poll_queue(&vsock->vqs[VSOCK_VQ_TX].poll);
+
mutex_unlock(&vsock->dev.mutex);
return 0;
@@ -664,9 +672,24 @@ err:
return ret;
}
-static int vhost_vsock_stop(struct vhost_vsock *vsock, bool check_owner)
+static void vhost_vsock_drop_backends(struct vhost_vsock *vsock)
{
+ struct vhost_virtqueue *vq;
size_t i;
+
+ lockdep_assert_held(&vsock->dev.mutex);
+
+ for (i = 0; i < ARRAY_SIZE(vsock->vqs); i++) {
+ vq = &vsock->vqs[i];
+
+ mutex_lock(&vq->mutex);
+ vhost_vq_set_backend(vq, NULL);
+ mutex_unlock(&vq->mutex);
+ }
+}
+
+static int vhost_vsock_stop(struct vhost_vsock *vsock, bool check_owner)
+{
int ret = 0;
mutex_lock(&vsock->dev.mutex);
@@ -677,14 +700,7 @@ static int vhost_vsock_stop(struct vhost_vsock *vsock, bool check_owner)
goto err;
}
- for (i = 0; i < ARRAY_SIZE(vsock->vqs); i++) {
- struct vhost_virtqueue *vq = &vsock->vqs[i];
-
- mutex_lock(&vq->mutex);
- vhost_vq_set_backend(vq, NULL);
- mutex_unlock(&vq->mutex);
- }
-
+ vhost_vsock_drop_backends(vsock);
err:
mutex_unlock(&vsock->dev.mutex);
return ret;
@@ -720,6 +736,7 @@ static int vhost_vsock_dev_open(struct inode *inode, struct file *file)
vsock->guest_cid = 0; /* no CID assigned yet */
vsock->seqpacket_allow = false;
+ vsock->ever_started = false;
atomic_set(&vsock->queued_replies, 0);
@@ -886,6 +903,29 @@ err:
return -EFAULT;
}
+static long vhost_vsock_reset_owner(struct vhost_vsock *vsock)
+{
+ struct vhost_iotlb *umem;
+ long err;
+
+ mutex_lock(&vsock->dev.mutex);
+ err = vhost_dev_check_owner(&vsock->dev);
+ if (err)
+ goto done;
+ umem = vhost_dev_reset_owner_prepare();
+ if (!umem) {
+ err = -ENOMEM;
+ goto done;
+ }
+ vhost_vsock_drop_backends(vsock);
+ vhost_vsock_flush(vsock);
+ vhost_dev_stop(&vsock->dev);
+ vhost_dev_reset_owner(&vsock->dev, umem);
+done:
+ mutex_unlock(&vsock->dev.mutex);
+ return err;
+}
+
static long vhost_vsock_dev_ioctl(struct file *f, unsigned int ioctl,
unsigned long arg)
{
@@ -929,6 +969,8 @@ static long vhost_vsock_dev_ioctl(struct file *f, unsigned int ioctl,
return -EOPNOTSUPP;
vhost_set_backend_features(&vsock->dev, features);
return 0;
+ case VHOST_RESET_OWNER:
+ return vhost_vsock_reset_owner(vsock);
default:
mutex_lock(&vsock->dev.mutex);
r = vhost_dev_ioctl(&vsock->dev, ioctl, argp);
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index ab3e3d887e00..5cc73dd1363c 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -617,25 +617,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))
@@ -922,6 +906,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;
@@ -1062,6 +1073,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;
@@ -1170,6 +1183,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);