diff options
| author | Mark Brown <broonie@kernel.org> | 2026-08-21 14:23:40 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-08-21 14:23:40 +0100 |
| commit | ebd7776db5abf0d5f37a9e7365430376fee90f50 (patch) | |
| tree | e719e40f64e6c15131e87722bdc679bbbf4177bc /drivers | |
| parent | 29dd393351d25ad4362f3eab3a7b9ea241aa67e1 (diff) | |
| parent | ae864da7d762b4c692e2cdf83cad43110d5d46ee (diff) | |
| download | linux-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.c | 11 | ||||
| -rw-r--r-- | drivers/vhost/vsock.c | 80 | ||||
| -rw-r--r-- | drivers/virtio/virtio_balloon.c | 51 |
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); |
