vhost,virtio: fixes, features, cleanups

This adds CPR support for vhost.
 
 Plus some fixes, notably for a host of races we had when callback fire
 around device reset.
 
 Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 
 iQFDBAABCgAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmrXRLEPHG1zdEByZWRo
 YXQuY29tAAoJECgfDbjSjVRpknAIAL7bxTwzZSTv5VbySGOCVDPZ4mysxRsKYbRT
 BTkDdYG+L4gvfwOMCuD2s3PIksNZwNvHCxYM9tsAOBQ4XgqOLW/GF07TifRMWUVz
 KFvl2wyEBuQb5ewKLgsASohHZ/2Bl+4qaL6RAuBN6KPQo5wYRCHlJEdh2IugWyIv
 /wUwBaO8u4pNHionj840TQcjjseYwnPBSG7V8aTRNXKcmiIQ7qqAx/88c7/2tdse
 FTSv7Y67LR02jVkiNTwJu0uFEGBNfzT3E47F/TPA53mJ53MLbW5Lej9kdlElKEQk
 19XOYulcLdoPhuMyTJ7/pX1Zf7BsYydTAYBaNerb8w4+9NPXEqk=
 =xkKp
 -----END PGP SIGNATURE-----

Merge tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost

Pull vhost,virtio updates from Michael Tsirkin:
 "This adds CPR support for vhost.

  Plus some fixes, notably for a host of races we had when callback fire
  around device reset"

* tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost:
  tools/virtio: add missing device-id/virtio.h wrapper header
  vhost/vsock: add VHOST_RESET_OWNER ioctl
  vhost: synchronize with RCU readers when freeing workers
  vhost/vsock: re-scan TX virtqueue on device start
  vhost/vsock: suppress EHOSTUNREACH fast-fail during CPR pause
  vhost/vsock: split out vhost_vsock_drop_backends helper
  virtio_pci: drop callback sync on reset
  virtio_pci_modern: move avq cleanup from reset to del_vqs
  virtio: synchronize callbacks after device reset
  virtio_vdpa: implement synchronize_cbs()
  platform/mellanox: mlxbf-tmfifo: implement synchronize_cbs()
  um: virtio_uml: implement synchronize_cbs()
  remoteproc: implement synchronize_cbs() for virtio devices
  virtio_ccw: always take irq_lock in the classic interrupt handler
  virtio_ccw: fix synchronize_cbs() after interrupt fallback
This commit is contained in:
Linus Torvalds 2026-10-23 14:19:27 -10:00
commit 9f2516f45b
18 changed files with 215 additions and 59 deletions

View File

@ -20,6 +20,7 @@
*
* Based on Virtio MMIO driver by Pawel Moll, copyright 2011-2014, ARM Ltd.
*/
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@ -869,6 +870,14 @@ static void vu_reset(struct virtio_device *vdev)
vu_dev->status = 0;
}
static void vu_synchronize_cbs(struct virtio_device *vdev)
{
struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev);
if (vu_dev->irq != UM_IRQ_ALLOC)
synchronize_irq(vu_dev->irq);
}
static void vu_del_vq(struct virtqueue *vq)
{
struct virtio_uml_vq_info *info = vq->priv;
@ -1121,6 +1130,7 @@ static const struct virtio_config_ops virtio_uml_config_ops = {
.reset = vu_reset,
.find_vqs = vu_find_vqs,
.del_vqs = vu_del_vqs,
.synchronize_cbs = vu_synchronize_cbs,
.get_features = vu_get_features,
.finalize_features = vu_finalize_features,
.bus_name = vu_bus_name,

View File

@ -1135,6 +1135,20 @@ static void mlxbf_tmfifo_virtio_reset(struct virtio_device *vdev)
tm_vdev->status = 0;
}
/* Synchronize with callbacks running in the FIFO work item. */
static void mlxbf_tmfifo_virtio_synchronize_cbs(struct virtio_device *vdev)
{
struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev);
struct mlxbf_tmfifo *fifo = tm_vdev->vrings[0].fifo;
unsigned long flags;
int i;
for (i = 0; i < ARRAY_SIZE(fifo->spin_lock); i++) {
spin_lock_irqsave(&fifo->spin_lock[i], flags);
spin_unlock_irqrestore(&fifo->spin_lock[i], flags);
}
}
/* Read the value of a configuration field. */
static void mlxbf_tmfifo_virtio_get(struct virtio_device *vdev,
unsigned int offset,
@ -1179,6 +1193,7 @@ static const struct virtio_config_ops mlxbf_tmfifo_virtio_config_ops = {
.find_vqs = mlxbf_tmfifo_virtio_find_vqs,
.del_vqs = mlxbf_tmfifo_virtio_del_vqs,
.reset = mlxbf_tmfifo_virtio_reset,
.synchronize_cbs = mlxbf_tmfifo_virtio_synchronize_cbs,
.set_status = mlxbf_tmfifo_virtio_set_status,
.get_status = mlxbf_tmfifo_virtio_get_status,
.get = mlxbf_tmfifo_virtio_get,

View File

@ -2410,6 +2410,7 @@ static void rproc_type_release(struct device *dev)
dev_info(&rproc->dev, "releasing %s\n", rproc->name);
cleanup_srcu_struct(&rproc->vq_srcu);
idr_destroy(&rproc->notifyids);
if (rproc->index >= 0)
@ -2507,6 +2508,11 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
if (!rproc)
return NULL;
if (init_srcu_struct(&rproc->vq_srcu)) {
kfree(rproc);
return NULL;
}
rproc->priv = &rproc[1];
rproc->auto_boot = true;
rproc->elf_class = ELFCLASSNONE;
@ -2571,6 +2577,9 @@ EXPORT_SYMBOL(rproc_alloc);
*
* If no one holds any reference to rproc anymore, then its refcount would
* now drop to zero, and it would be freed.
*
* Context: Any context, but the last reference must not be dropped from
* atomic context.
*/
void rproc_free(struct rproc *rproc)
{
@ -2586,6 +2595,9 @@ EXPORT_SYMBOL(rproc_free);
*
* If no one holds any reference to rproc anymore, then its refcount would
* now drop to zero, and it would be freed.
*
* Context: Any context, but the last reference must not be dropped from
* atomic context.
*/
void rproc_put(struct rproc *rproc)
{

View File

@ -23,6 +23,7 @@
#include <linux/err.h>
#include <linux/kref.h>
#include <linux/slab.h>
#include <linux/srcu.h>
#include "remoteproc_internal.h"
@ -88,15 +89,23 @@ static bool rproc_virtio_notify(struct virtqueue *vq)
*/
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid)
{
irqreturn_t ret = IRQ_NONE;
struct rproc_vring *rvring;
struct virtqueue *vq;
int idx;
dev_dbg(&rproc->dev, "vq index %d is interrupted\n", notifyid);
rvring = idr_find(&rproc->notifyids, notifyid);
if (!rvring || !rvring->vq)
return IRQ_NONE;
idx = srcu_read_lock(&rproc->vq_srcu);
return vring_interrupt(0, rvring->vq);
rvring = idr_find(&rproc->notifyids, notifyid);
vq = rvring ? READ_ONCE(rvring->vq) : NULL;
if (vq)
ret = vring_interrupt(0, vq);
srcu_read_unlock(&rproc->vq_srcu, idx);
return ret;
}
EXPORT_SYMBOL(rproc_vq_interrupt);
@ -153,7 +162,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
vq->num_max = num;
rvring->vq = vq;
WRITE_ONCE(rvring->vq, vq);
vq->priv = rvring;
/* Update vring in resource table */
@ -165,14 +174,20 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
static void __rproc_virtio_del_vqs(struct virtio_device *vdev)
{
struct rproc *rproc = vdev_to_rproc(vdev);
struct virtqueue *vq, *n;
struct rproc_vring *rvring;
list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
list_for_each_entry(vq, &vdev->vqs, list) {
rvring = vq->priv;
rvring->vq = NULL;
vring_del_virtqueue(vq);
WRITE_ONCE(rvring->vq, NULL);
}
/* Synchronize with rproc_vq_interrupt() callers that found a queue. */
synchronize_srcu(&rproc->vq_srcu);
list_for_each_entry_safe(vq, n, &vdev->vqs, list)
vring_del_virtqueue(vq);
}
static void rproc_virtio_del_vqs(struct virtio_device *vdev)
@ -242,6 +257,13 @@ static void rproc_virtio_reset(struct virtio_device *vdev)
dev_dbg(&vdev->dev, "reset !\n");
}
static void rproc_virtio_synchronize_cbs(struct virtio_device *vdev)
{
struct rproc *rproc = vdev_to_rproc(vdev);
synchronize_srcu(&rproc->vq_srcu);
}
/* provide the vdev features as retrieved from the firmware */
static u64 rproc_virtio_get_features(struct virtio_device *vdev)
{
@ -330,6 +352,7 @@ static const struct virtio_config_ops rproc_virtio_config_ops = {
.find_vqs = rproc_virtio_find_vqs,
.del_vqs = rproc_virtio_del_vqs,
.reset = rproc_virtio_reset,
.synchronize_cbs = rproc_virtio_synchronize_cbs,
.set_status = rproc_virtio_set_status,
.get_status = rproc_virtio_get_status,
.get = rproc_virtio_get,

View File

@ -1062,7 +1062,7 @@ static void virtio_ccw_synchronize_cbs(struct virtio_device *vdev)
struct virtio_ccw_device *vcdev = to_vc_device(vdev);
struct airq_info *info = vcdev->airq_info;
if (info) {
if (vcdev->is_thinint && info) {
/*
* This device uses adapter interrupts: synchronize with
* vring_interrupt() called by virtio_airq_handler()
@ -1204,13 +1204,11 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev,
vcdev->err = -EIO;
}
virtio_ccw_check_activity(vcdev, activity);
#ifdef CONFIG_VIRTIO_HARDEN_NOTIFICATION
/*
* Paired with virtio_ccw_synchronize_cbs() and interrupts are
* disabled here.
*/
read_lock(&vcdev->irq_lock);
#endif
for_each_set_bit(i, indicators(vcdev),
sizeof(*indicators(vcdev)) * BITS_PER_BYTE) {
/* The bit clear must happen before the vring kick. */
@ -1219,9 +1217,7 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev,
vq = virtio_ccw_vq_by_ind(vcdev, i);
vring_interrupt(0, vq);
}
#ifdef CONFIG_VIRTIO_HARDEN_NOTIFICATION
read_unlock(&vcdev->irq_lock);
#endif
if (test_bit(0, indicators2(vcdev))) {
virtio_config_changed(&vcdev->vdev);
clear_bit(0, indicators2(vcdev));

View File

@ -1452,7 +1452,7 @@ static int vhost_net_release(struct inode *inode, struct file *f)
vhost_net_stop(n, &tx_sock, &rx_sock);
vhost_net_flush(n);
vhost_dev_stop(&n->dev);
vhost_dev_cleanup(&n->dev);
vhost_dev_cleanup(&n->dev, false);
vhost_net_vq_reset(n);
if (tx_sock)
sockfd_put(tx_sock);
@ -1661,7 +1661,7 @@ static long vhost_net_reset_owner(struct vhost_net *n)
vhost_net_stop(n, &tx_sock, &rx_sock);
vhost_net_flush(n);
vhost_dev_stop(&n->dev);
vhost_dev_reset_owner(&n->dev, umem);
vhost_dev_reset_owner(&n->dev, umem, false);
vhost_net_vq_reset(n);
done:
mutex_unlock(&n->dev.mutex);

View File

@ -2390,7 +2390,7 @@ static int vhost_scsi_release(struct inode *inode, struct file *f)
mutex_unlock(&vs->dev.mutex);
vhost_scsi_clear_endpoint(vs, &t);
vhost_dev_stop(&vs->dev);
vhost_dev_cleanup(&vs->dev);
vhost_dev_cleanup(&vs->dev, false);
kfree(vs->dev.vqs);
kvfree(vs->vqs);
kfree(vs->old_inflight);

View File

@ -163,7 +163,7 @@ static int vhost_test_release(struct inode *inode, struct file *f)
vhost_test_stop(n, &private);
vhost_test_flush(n);
vhost_dev_stop(&n->dev);
vhost_dev_cleanup(&n->dev);
vhost_dev_cleanup(&n->dev, false);
kfree(n->dev.vqs);
kfree(n);
return 0;
@ -238,7 +238,7 @@ static long vhost_test_reset_owner(struct vhost_test *n)
vhost_test_stop(n, &priv);
vhost_test_flush(n);
vhost_dev_stop(&n->dev);
vhost_dev_reset_owner(&n->dev, umem);
vhost_dev_reset_owner(&n->dev, umem, false);
done:
mutex_unlock(&n->dev.mutex);
return err;

View File

@ -948,7 +948,7 @@ static long vhost_vdpa_unlocked_ioctl(struct file *filep,
case VHOST_SET_OWNER:
r = vhost_vdpa_bind_mm(v);
if (r)
vhost_dev_reset_owner(d, NULL);
vhost_dev_reset_owner(d, NULL, false);
break;
}
out:
@ -1453,7 +1453,7 @@ static void vhost_vdpa_cleanup(struct vhost_vdpa *v)
}
vhost_vdpa_free_domain(v);
vhost_dev_cleanup(&v->vdev);
vhost_dev_cleanup(&v->vdev, false);
kfree(v->vdev.vqs);
v->vdev.vqs = NULL;
}

View File

@ -731,7 +731,7 @@ static void vhost_worker_destroy(struct vhost_dev *dev,
kfree(worker);
}
static void vhost_workers_free(struct vhost_dev *dev)
static void vhost_workers_free(struct vhost_dev *dev, bool sync)
{
struct vhost_worker *worker;
unsigned long i;
@ -741,6 +741,21 @@ static void vhost_workers_free(struct vhost_dev *dev)
for (i = 0; i < dev->nvqs; i++)
rcu_assign_pointer(dev->vqs[i]->worker, NULL);
/*
* This path can be reached by lockless work queuers. In this case
* vhost_vq_work_queue() reads vq->worker under rcu_read_lock(), so a
* RCU 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. Notably, that is the case for the
* vsock RESET_OWNER path.
*/
if (sync) {
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).
@ -1163,11 +1178,12 @@ struct vhost_iotlb *vhost_dev_reset_owner_prepare(void)
EXPORT_SYMBOL_GPL(vhost_dev_reset_owner_prepare);
/* Caller should have device mutex */
void vhost_dev_reset_owner(struct vhost_dev *dev, struct vhost_iotlb *umem)
void vhost_dev_reset_owner(struct vhost_dev *dev, struct vhost_iotlb *umem,
bool sync)
{
int i;
vhost_dev_cleanup(dev);
vhost_dev_cleanup(dev, sync);
dev->fork_owner = fork_from_owner_default;
dev->umem = umem;
@ -1227,7 +1243,7 @@ void vhost_clear_msg(struct vhost_dev *dev)
}
EXPORT_SYMBOL_GPL(vhost_clear_msg);
void vhost_dev_cleanup(struct vhost_dev *dev)
void vhost_dev_cleanup(struct vhost_dev *dev, bool sync)
{
int i;
@ -1251,7 +1267,7 @@ void vhost_dev_cleanup(struct vhost_dev *dev)
dev->iotlb = NULL;
vhost_clear_msg(dev);
wake_up_interruptible_poll(&dev->wait, EPOLLIN | EPOLLRDNORM);
vhost_workers_free(dev);
vhost_workers_free(dev, sync);
vhost_detach_mm(dev);
}
EXPORT_SYMBOL_GPL(vhost_dev_cleanup);

View File

@ -219,8 +219,9 @@ long vhost_dev_set_owner(struct vhost_dev *dev);
bool vhost_dev_has_owner(struct vhost_dev *dev);
long vhost_dev_check_owner(struct vhost_dev *);
struct vhost_iotlb *vhost_dev_reset_owner_prepare(void);
void vhost_dev_reset_owner(struct vhost_dev *dev, struct vhost_iotlb *iotlb);
void vhost_dev_cleanup(struct vhost_dev *);
void vhost_dev_reset_owner(struct vhost_dev *dev, struct vhost_iotlb *iotlb,
bool sync);
void vhost_dev_cleanup(struct vhost_dev *dev, bool sync);
void vhost_dev_stop(struct vhost_dev *);
long vhost_dev_ioctl(struct vhost_dev *, unsigned int ioctl, void __user *argp);
long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *argp);

View File

@ -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 @@ static int vhost_vsock_start(struct vhost_vsock *vsock)
return ret;
}
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)
{
size_t i;
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);
@ -807,7 +824,7 @@ static int vhost_vsock_dev_release(struct inode *inode, struct file *file)
virtio_vsock_skb_queue_purge(&vsock->send_pkt_queue);
vhost_dev_cleanup(&vsock->dev);
vhost_dev_cleanup(&vsock->dev, false);
put_net_track(vsock->net, &vsock->ns_tracker);
kfree(vsock->dev.vqs);
vhost_vsock_free(vsock);
@ -888,6 +905,36 @@ static int vhost_vsock_set_features(struct vhost_vsock *vsock, u64 features)
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);
/*
* vsock keeps the CID hashed across RESET_OWNER, so a lockless sender
* can still reach the device while its workers are freed. Pass
* sync=true so vhost_workers_free() fences RCU readers and flushes
* before freeing them.
*/
vhost_dev_reset_owner(&vsock->dev, umem, true);
done:
mutex_unlock(&vsock->dev.mutex);
return err;
}
static long vhost_vsock_dev_ioctl(struct file *f, unsigned int ioctl,
unsigned long arg)
{
@ -931,6 +978,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);

View File

@ -264,6 +264,8 @@ void virtio_reset_device(struct virtio_device *dev)
#endif
dev->config->reset(dev);
/* Flush pending VQ/configuration callbacks. */
virtio_synchronize_cbs(dev);
}
EXPORT_SYMBOL_GPL(virtio_reset_device);

View File

@ -98,8 +98,6 @@ static void vp_reset(struct virtio_device *vdev)
/* Flush out the status write, and flush in device writes,
* including MSi-X interrupts, if any. */
vp_legacy_get_status(&vp_dev->ldev);
/* Flush pending VQ/configuration callbacks. */
vp_synchronize_vectors(vdev);
}
static u16 vp_config_vector(struct virtio_pci_device *vp_dev, u16 vector)

View File

@ -364,6 +364,12 @@ static void vp_modern_avq_cleanup(struct virtio_device *vdev)
}
}
static void vp_modern_del_vqs(struct virtio_device *vdev)
{
vp_modern_avq_cleanup(vdev);
vp_del_vqs(vdev);
}
static void vp_transport_features(struct virtio_device *vdev, u64 features)
{
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
@ -557,11 +563,6 @@ static void vp_reset(struct virtio_device *vdev)
*/
while (vp_modern_get_status(mdev))
msleep(1);
vp_modern_avq_cleanup(vdev);
/* Flush pending VQ/configuration callbacks. */
vp_synchronize_vectors(vdev);
}
static int vp_active_vq(struct virtqueue *vq, u16 msix_vec)
@ -1232,7 +1233,7 @@ static const struct virtio_config_ops virtio_pci_config_nodev_ops = {
.set_status = vp_set_status,
.reset = vp_reset,
.find_vqs = vp_modern_find_vqs,
.del_vqs = vp_del_vqs,
.del_vqs = vp_modern_del_vqs,
.synchronize_cbs = vp_synchronize_vectors,
.get_extended_features = vp_get_features,
.finalize_features = vp_finalize_features,
@ -1252,7 +1253,7 @@ static const struct virtio_config_ops virtio_pci_config_ops = {
.set_status = vp_set_status,
.reset = vp_reset,
.find_vqs = vp_modern_find_vqs,
.del_vqs = vp_del_vqs,
.del_vqs = vp_modern_del_vqs,
.synchronize_cbs = vp_synchronize_vectors,
.get_extended_features = vp_get_features,
.finalize_features = vp_finalize_features,

View File

@ -12,6 +12,7 @@
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/srcu.h>
#include <linux/uuid.h>
#include <linux/group_cpus.h>
#include <linux/virtio.h>
@ -27,6 +28,7 @@
struct virtio_vdpa_device {
struct virtio_device vdev;
struct vdpa_device *vdpa;
struct srcu_struct cb_srcu;
u64 features;
};
@ -114,8 +116,11 @@ static bool virtio_vdpa_notify_with_data(struct virtqueue *vq)
static irqreturn_t virtio_vdpa_config_cb(void *private)
{
struct virtio_vdpa_device *vd_dev = private;
int idx;
idx = srcu_read_lock(&vd_dev->cb_srcu);
virtio_config_changed(&vd_dev->vdev);
srcu_read_unlock(&vd_dev->cb_srcu, idx);
return IRQ_HANDLED;
}
@ -123,8 +128,22 @@ static irqreturn_t virtio_vdpa_config_cb(void *private)
static irqreturn_t virtio_vdpa_virtqueue_cb(void *private)
{
struct virtqueue *vq = private;
struct virtio_vdpa_device *vd_dev = to_virtio_vdpa_device(vq->vdev);
irqreturn_t ret;
int idx;
return vring_interrupt(0, vq);
idx = srcu_read_lock(&vd_dev->cb_srcu);
ret = vring_interrupt(0, vq);
srcu_read_unlock(&vd_dev->cb_srcu, idx);
return ret;
}
static void virtio_vdpa_synchronize_cbs(struct virtio_device *vdev)
{
struct virtio_vdpa_device *vd_dev = to_virtio_vdpa_device(vdev);
synchronize_srcu(&vd_dev->cb_srcu);
}
static struct virtqueue *
@ -440,6 +459,7 @@ static const struct virtio_config_ops virtio_vdpa_config_ops = {
.reset = virtio_vdpa_reset,
.find_vqs = virtio_vdpa_find_vqs,
.del_vqs = virtio_vdpa_del_vqs,
.synchronize_cbs = virtio_vdpa_synchronize_cbs,
.get_features = virtio_vdpa_get_features,
.finalize_features = virtio_vdpa_finalize_features,
.bus_name = virtio_vdpa_bus_name,
@ -454,6 +474,7 @@ static void virtio_vdpa_release_dev(struct device *_d)
struct virtio_vdpa_device *vd_dev =
container_of(vdev, struct virtio_vdpa_device, vdev);
cleanup_srcu_struct(&vd_dev->cb_srcu);
kfree(vd_dev);
}
@ -467,6 +488,11 @@ static int virtio_vdpa_probe(struct vdpa_device *vdpa)
if (!vd_dev)
return -ENOMEM;
if (init_srcu_struct(&vd_dev->cb_srcu)) {
kfree(vd_dev);
return -ENOMEM;
}
vd_dev->vdev.dev.parent = vdpa->map ? &vdpa->dev :
vdpa_get_map(vdpa).dma_dev;
vd_dev->vdev.dev.release = virtio_vdpa_release_dev;
@ -489,10 +515,12 @@ static int virtio_vdpa_probe(struct vdpa_device *vdpa)
return 0;
err:
if (reg_dev)
if (reg_dev) {
put_device(&vd_dev->vdev.dev);
else
} else {
cleanup_srcu_struct(&vd_dev->cb_srcu);
kfree(vd_dev);
}
return ret;
}

View File

@ -15,6 +15,7 @@
#include <linux/cdev.h>
#include <linux/completion.h>
#include <linux/idr.h>
#include <linux/srcu.h>
#include <linux/of.h>
#include <linux/rsc_table.h>
@ -228,6 +229,7 @@ enum rproc_features {
* @mappings: list of iommu mappings we initiated, needed on shutdown
* @bootaddr: address of first instruction to boot rproc with (optional)
* @rvdevs: list of remote virtio devices
* @vq_srcu: SRCU domain for the virtqueue callbacks of @rvdevs
* @subdevs: list of subdevices, to following the running state
* @notifyids: idr for dynamically assigning rproc-wide unique notify ids
* @index: index of this rproc device
@ -274,6 +276,7 @@ struct rproc {
struct list_head mappings;
u64 bootaddr;
struct list_head rvdevs;
struct srcu_struct vq_srcu;
struct list_head subdevs;
struct idr notifyids;
int index;

View File

@ -0,0 +1,2 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <../../include/linux/device-id/virtio.h>