mirror of
https://github.com/torvalds/linux.git
synced 2026-09-11 20:13:02 +02:00
vhost,vdpa,virtio: fixes
Just a ton of small fixes all over the place. Also includes virtio and virtio-rng MAINTAINERS updates. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> -----BEGIN PGP SIGNATURE----- iQFDBAABCgAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmqhKYcPHG1zdEByZWRo YXQuY29tAAoJECgfDbjSjVRppBUIAK/QswxhFU0fUQPFQ4YU5xB8/ANGBBpaE1D0 D6g7LYJsB9SguzdiSWOK8BV9/2h8A485yoU98kQBHLCM/Qraclr/t8sNel0Vq3V/ FZmCW21EQZnbcsEbct5WlBlU2veUP2mAhBlRruHEFdMil/W2k4ifF26jFnKAgS8y ixirBte0LRCo/Ho42D2mZrY40Z1viRKL03Uhl4jiJz+16bx8uRWGd0UELjr7fMT0 095HUYvOdCFLLedhFe9LFN5VFb+gy/iQjv/wOBAcwjjwHNIIAF40gedu8bRh8ovu ZA17syGe5DjLnp/C1nnEwZhGSDngVEC/aBogKXKJ1MHtVbVZn+A= =3WXI -----END PGP SIGNATURE----- Merge tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost Pull virtio fixes from Michael Tsirkin: "Just a ton of small fixes all over the place. Also includes virtio and virtio-rng MAINTAINERS updates" * tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost: (27 commits) vduse: return compat ioctl results directly virtio_input: stop callbacks before unregistering input device virtio_input: reset device if input_register_device() fails vhost: invalidate vring access on IOTLB transitions vduse: validate virtqueue alignment vduse: do not take dev->rwsem in the virtqueue kick path vhost-scsi: clamp max_io_vqs module parameter vhost-scsi: use kvzalloc for vq array allocation virtio-pci: return IRQ_HANDLED after non-zero ISR virtio: add Eugenio Pérez as Maintainer vhost: limit outstanding IOTLB misses per virtqueue MAINTAINERS: Add a section for virtio-rng vdpa_sim_net: check TX pull result before RX copy vdpa_sim_blk: reject out-of-range sector starts virtio-vdpa: Use queue id when setting vq affinity vdpa: octeon_ep: Check dev_set_name() in dev add vdpa: ifcvf: Put device on unsupported feature error vdpa: solidrun: Free IRQs after request failure vdpa: alibaba: Keep DRIVER_OK clear if IRQ setup fails vdpa/pds: check virtqueue notify mapping ...
This commit is contained in:
commit
4f3989d75d
13
MAINTAINERS
13
MAINTAINERS
|
|
@ -28869,8 +28869,8 @@ F: include/uapi/linux/virtio_console.h
|
|||
VIRTIO CORE
|
||||
M: "Michael S. Tsirkin" <mst@redhat.com>
|
||||
M: Jason Wang <jasowangio@gmail.com>
|
||||
M: Eugenio Pérez <eperezma@redhat.com>
|
||||
R: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
|
||||
R: Eugenio Pérez <eperezma@redhat.com>
|
||||
L: virtualization@lists.linux.dev
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-vdpa
|
||||
|
|
@ -28947,7 +28947,7 @@ F: include/uapi/linux/virtio_gpu.h
|
|||
VIRTIO HOST (VHOST)
|
||||
M: "Michael S. Tsirkin" <mst@redhat.com>
|
||||
M: Jason Wang <jasowangio@gmail.com>
|
||||
R: Eugenio Pérez <eperezma@redhat.com>
|
||||
M: Eugenio Pérez <eperezma@redhat.com>
|
||||
L: kvm@vger.kernel.org
|
||||
L: virtualization@lists.linux.dev
|
||||
L: netdev@vger.kernel.org
|
||||
|
|
@ -29002,8 +29002,8 @@ F: include/uapi/linux/virtio_mem.h
|
|||
VIRTIO NET DRIVER
|
||||
M: "Michael S. Tsirkin" <mst@redhat.com>
|
||||
M: Jason Wang <jasowangio@gmail.com>
|
||||
M: Eugenio Pérez <eperezma@redhat.com>
|
||||
R: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
|
||||
R: Eugenio Pérez <eperezma@redhat.com>
|
||||
L: netdev@vger.kernel.org
|
||||
L: virtualization@lists.linux.dev
|
||||
S: Maintained
|
||||
|
|
@ -29019,6 +29019,13 @@ S: Maintained
|
|||
F: drivers/nvdimm/nd_virtio.c
|
||||
F: drivers/nvdimm/virtio_pmem.c
|
||||
|
||||
VIRTIO RNG DRIVER
|
||||
M: Laurent Vivier <lvivier@redhat.com>
|
||||
L: virtualization@lists.linux.dev
|
||||
S: Maintained
|
||||
F: drivers/char/hw_random/virtio-rng.c
|
||||
F: include/uapi/linux/virtio_rng.h
|
||||
|
||||
VIRTIO RTC DRIVER
|
||||
M: Peter Hilber <peter.hilber@oss.qualcomm.com>
|
||||
L: virtualization@lists.linux.dev
|
||||
|
|
|
|||
|
|
@ -1964,13 +1964,28 @@ static const struct file_operations portdev_fops = {
|
|||
static void remove_vqs(struct ports_device *portdev)
|
||||
{
|
||||
struct virtqueue *vq;
|
||||
bool multiport = use_multiport(portdev);
|
||||
|
||||
virtio_device_for_each_vq(portdev->vdev, vq) {
|
||||
struct port_buffer *buf;
|
||||
unsigned int len;
|
||||
|
||||
flush_bufs(vq, true);
|
||||
while ((buf = virtqueue_detach_unused_buf(vq)))
|
||||
free_buf(buf, true);
|
||||
/*
|
||||
* c_ovq cookies are &portdev->cpkt, not port_buffer.
|
||||
* Detach them but do not free_buf().
|
||||
*/
|
||||
if (multiport && vq == portdev->c_ovq) {
|
||||
spin_lock(&portdev->c_ovq_lock);
|
||||
while (virtqueue_get_buf(vq, &len))
|
||||
;
|
||||
while (virtqueue_detach_unused_buf(vq))
|
||||
;
|
||||
spin_unlock(&portdev->c_ovq_lock);
|
||||
} else {
|
||||
flush_bufs(vq, true);
|
||||
while ((buf = virtqueue_detach_unused_buf(vq)))
|
||||
free_buf(buf, true);
|
||||
}
|
||||
cond_resched();
|
||||
}
|
||||
portdev->vdev->config->del_vqs(portdev->vdev);
|
||||
|
|
|
|||
|
|
@ -216,7 +216,10 @@ static void eni_vdpa_set_status(struct vdpa_device *vdpa, u8 status)
|
|||
|
||||
if (status & VIRTIO_CONFIG_S_DRIVER_OK &&
|
||||
!(s & VIRTIO_CONFIG_S_DRIVER_OK)) {
|
||||
eni_vdpa_request_irq(eni_vdpa);
|
||||
if (eni_vdpa_request_irq(eni_vdpa)) {
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
vp_legacy_set_status(ldev, status);
|
||||
|
|
|
|||
|
|
@ -724,7 +724,8 @@ static int ifcvf_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name,
|
|||
if (config->device_features & ~device_features) {
|
||||
IFCVF_ERR(pdev, "The provisioned features 0x%llx are not supported by this device with features 0x%llx\n",
|
||||
config->device_features, device_features);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
device_features &= config->device_features;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -600,6 +600,8 @@ static int octep_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name,
|
|||
ret = dev_set_name(&vdpa_dev->dev, "%s", name);
|
||||
else
|
||||
ret = dev_set_name(&vdpa_dev->dev, "vdpa%u", vdpa_dev->index);
|
||||
if (ret)
|
||||
goto vdpa_dev_put;
|
||||
|
||||
ret = _vdpa_register_device(&oct_vdpa->vdpa, oct_hw->nr_vring);
|
||||
if (ret) {
|
||||
|
|
|
|||
|
|
@ -731,6 +731,12 @@ static int pds_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name,
|
|||
|
||||
notify = vp_modern_map_vq_notify(&pdsv->vdpa_aux->vd_mdev,
|
||||
i, &pdsv->vqs[i].notify_pa);
|
||||
if (!notify) {
|
||||
err = -EINVAL;
|
||||
dev_err(dev, "Fail to map vq notify %d\n", i);
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
pds_vdpa_init_vqs_entry(pdsv, i, notify);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -418,11 +418,15 @@ static int snet_request_irqs(struct pci_dev *pdev, struct snet *snet)
|
|||
snet->vqs[i]->irq_name, snet->vqs[i]);
|
||||
if (ret) {
|
||||
SNET_ERR(pdev, "Failed to request IRQ\n");
|
||||
return ret;
|
||||
goto err_free_irqs;
|
||||
}
|
||||
snet->vqs[i]->irq = irq;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_free_irqs:
|
||||
snet_free_irqs(snet);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void snet_set_status(struct vdpa_device *vdev, u8 status)
|
||||
|
|
|
|||
|
|
@ -79,10 +79,11 @@ static void vdpasim_blk_buffer_unlock(struct vdpasim_blk *blk)
|
|||
static bool vdpasim_blk_check_range(struct vdpasim *vdpasim, u64 start_sector,
|
||||
u64 num_sectors, u64 max_sectors)
|
||||
{
|
||||
if (start_sector > VDPASIM_BLK_CAPACITY) {
|
||||
if (start_sector >= VDPASIM_BLK_CAPACITY) {
|
||||
dev_dbg(&vdpasim->vdpa.dev,
|
||||
"starting sector exceeds the capacity - start: 0x%llx capacity: 0x%x\n",
|
||||
start_sector, VDPASIM_BLK_CAPACITY);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (num_sectors > max_sectors) {
|
||||
|
|
|
|||
|
|
@ -225,10 +225,15 @@ static void vdpasim_net_work(struct vdpasim *vdpasim)
|
|||
break;
|
||||
}
|
||||
|
||||
++tx_pkts;
|
||||
read = vringh_iov_pull_iotlb(&txq->vring, &txq->out_iov,
|
||||
net->buffer, PAGE_SIZE);
|
||||
if (read <= 0) {
|
||||
++tx_errors;
|
||||
vdpasim_net_complete(txq, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
++tx_pkts;
|
||||
tx_bytes += read;
|
||||
|
||||
if (!receive_filter(vdpasim, read)) {
|
||||
|
|
|
|||
|
|
@ -506,7 +506,7 @@ static void vduse_dev_reset(struct vduse_dev *dev)
|
|||
}
|
||||
|
||||
scoped_guard(rwsem_write, &dev->rwsem) {
|
||||
dev->suspended = false;
|
||||
WRITE_ONCE(dev->suspended, false);
|
||||
dev->status = 0;
|
||||
dev->driver_features = 0;
|
||||
dev->generation++;
|
||||
|
|
@ -567,11 +567,17 @@ static int vduse_vdpa_set_vq_address(struct vdpa_device *vdpa, u16 idx,
|
|||
|
||||
static void vduse_vq_kick(struct vduse_virtqueue *vq)
|
||||
{
|
||||
guard(rwsem_read)(&vq->dev->rwsem);
|
||||
if (vq->dev->suspended)
|
||||
/*
|
||||
* This runs in the context of the vdpa kick_vq op, which may be
|
||||
* atomic (e.g. virtio-blk kicks from blk-mq dispatch under
|
||||
* rcu_read_lock()), so dev->rwsem must not be taken here.
|
||||
* dev->suspended is checked under kick_lock instead and
|
||||
* vduse_vdpa_suspend() cycles every kick_lock after setting it.
|
||||
*/
|
||||
guard(spinlock)(&vq->kick_lock);
|
||||
if (READ_ONCE(vq->dev->suspended))
|
||||
return;
|
||||
|
||||
guard(spinlock)(&vq->kick_lock);
|
||||
scoped_guard(spinlock_bh, &vq->ready_lock)
|
||||
if (!vq->ready)
|
||||
return;
|
||||
|
|
@ -946,7 +952,17 @@ static int vduse_vdpa_suspend(struct vdpa_device *vdpa)
|
|||
ret = vduse_dev_msg_sync(dev, &msg);
|
||||
if (ret == 0) {
|
||||
scoped_guard(rwsem_write, &dev->rwsem)
|
||||
dev->suspended = true;
|
||||
WRITE_ONCE(dev->suspended, true);
|
||||
|
||||
/*
|
||||
* Kicks check dev->suspended under kick_lock without taking
|
||||
* the rwsem: cycle each kick_lock so that no kick that has
|
||||
* already passed the check is still in flight after this.
|
||||
*/
|
||||
for (u32 i = 0; i < dev->vq_num; i++) {
|
||||
spin_lock(&dev->vqs[i]->kick_lock);
|
||||
spin_unlock(&dev->vqs[i]->kick_lock);
|
||||
}
|
||||
|
||||
cancel_work_sync(&dev->inject);
|
||||
for (u32 i = 0; i < dev->vq_num; i++)
|
||||
|
|
@ -1866,11 +1882,11 @@ static long vduse_dev_compat_ioctl(struct file *file, unsigned int cmd,
|
|||
break;
|
||||
}
|
||||
default:
|
||||
ret = -ENOIOCTLCMD;
|
||||
break;
|
||||
return vduse_dev_ioctl(file, cmd,
|
||||
(unsigned long)compat_ptr(arg));
|
||||
}
|
||||
|
||||
return vduse_dev_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#define vduse_dev_compat_ioctl compat_ptr_ioctl
|
||||
|
|
@ -2211,7 +2227,9 @@ static bool vduse_validate_config(struct vduse_dev_config *config,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (config->vq_align > PAGE_SIZE)
|
||||
if (config->vq_align < VRING_USED_ALIGN_SIZE ||
|
||||
!is_power_of_2(config->vq_align) ||
|
||||
config->vq_align > PAGE_SIZE)
|
||||
return false;
|
||||
|
||||
if (config->config_size > PAGE_SIZE)
|
||||
|
|
|
|||
|
|
@ -1705,6 +1705,8 @@ static int vhost_net_set_features(struct vhost_net *n, const u64 *features)
|
|||
if (virtio_features_test_bit(features, VIRTIO_F_ACCESS_PLATFORM)) {
|
||||
if (vhost_init_device_iotlb(&n->dev))
|
||||
goto out_unlock;
|
||||
} else {
|
||||
vhost_clear_device_iotlb(&n->dev);
|
||||
}
|
||||
|
||||
for (i = 0; i < VHOST_NET_VQ_MAX; ++i) {
|
||||
|
|
|
|||
|
|
@ -210,7 +210,37 @@ static const int vhost_scsi_bits[] = {
|
|||
#define VHOST_SCSI_MAX_EVENT 128
|
||||
|
||||
static unsigned vhost_scsi_max_io_vqs = 128;
|
||||
module_param_named(max_io_vqs, vhost_scsi_max_io_vqs, uint, 0644);
|
||||
|
||||
static int vhost_scsi_set_max_io_vqs(const char *val,
|
||||
const struct kernel_param *kp)
|
||||
{
|
||||
unsigned int max_io_vqs;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint(val, 0, &max_io_vqs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (max_io_vqs > VHOST_SCSI_MAX_IO_VQ) {
|
||||
pr_err("Invalid max_io_vqs of %u. Using %u.\n",
|
||||
max_io_vqs, VHOST_SCSI_MAX_IO_VQ);
|
||||
max_io_vqs = VHOST_SCSI_MAX_IO_VQ;
|
||||
} else if (!max_io_vqs) {
|
||||
pr_err("Invalid max_io_vqs of 0. Using 1.\n");
|
||||
max_io_vqs = 1;
|
||||
}
|
||||
|
||||
WRITE_ONCE(vhost_scsi_max_io_vqs, max_io_vqs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops vhost_scsi_max_io_vqs_op = {
|
||||
.set = vhost_scsi_set_max_io_vqs,
|
||||
.get = param_get_uint,
|
||||
};
|
||||
|
||||
module_param_cb(max_io_vqs, &vhost_scsi_max_io_vqs_op,
|
||||
&vhost_scsi_max_io_vqs, 0644);
|
||||
MODULE_PARM_DESC(max_io_vqs, "Set the max number of IO virtqueues a vhost scsi device can support. The default is 128. The max is 1024.");
|
||||
|
||||
struct vhost_scsi_virtqueue {
|
||||
|
|
@ -2290,21 +2320,14 @@ static int vhost_scsi_open(struct inode *inode, struct file *f)
|
|||
struct vhost_scsi_virtqueue *svq;
|
||||
struct vhost_scsi *vs;
|
||||
struct vhost_virtqueue **vqs;
|
||||
int r = -ENOMEM, i, nvqs = vhost_scsi_max_io_vqs;
|
||||
int r = -ENOMEM, i, nvqs;
|
||||
|
||||
vs = kvzalloc_obj(*vs);
|
||||
if (!vs)
|
||||
goto err_vs;
|
||||
vs->inline_sg_cnt = vhost_scsi_inline_sg_cnt;
|
||||
|
||||
if (nvqs > VHOST_SCSI_MAX_IO_VQ) {
|
||||
pr_err("Invalid max_io_vqs of %d. Using %d.\n", nvqs,
|
||||
VHOST_SCSI_MAX_IO_VQ);
|
||||
nvqs = VHOST_SCSI_MAX_IO_VQ;
|
||||
} else if (nvqs == 0) {
|
||||
pr_err("Invalid max_io_vqs of %d. Using 1.\n", nvqs);
|
||||
nvqs = 1;
|
||||
}
|
||||
nvqs = READ_ONCE(vhost_scsi_max_io_vqs);
|
||||
nvqs += VHOST_SCSI_VQ_IO;
|
||||
|
||||
vs->old_inflight = kmalloc_objs(*vs->old_inflight, nvqs,
|
||||
|
|
@ -2312,7 +2335,7 @@ static int vhost_scsi_open(struct inode *inode, struct file *f)
|
|||
if (!vs->old_inflight)
|
||||
goto err_inflight;
|
||||
|
||||
vs->vqs = kmalloc_objs(*vs->vqs, nvqs, GFP_KERNEL | __GFP_ZERO);
|
||||
vs->vqs = kvzalloc_objs(*vs->vqs, nvqs);
|
||||
if (!vs->vqs)
|
||||
goto err_vqs;
|
||||
|
||||
|
|
@ -2348,7 +2371,7 @@ static int vhost_scsi_open(struct inode *inode, struct file *f)
|
|||
return 0;
|
||||
|
||||
err_local_vqs:
|
||||
kfree(vs->vqs);
|
||||
kvfree(vs->vqs);
|
||||
err_vqs:
|
||||
kfree(vs->old_inflight);
|
||||
err_inflight:
|
||||
|
|
@ -2369,7 +2392,7 @@ static int vhost_scsi_release(struct inode *inode, struct file *f)
|
|||
vhost_dev_stop(&vs->dev);
|
||||
vhost_dev_cleanup(&vs->dev);
|
||||
kfree(vs->dev.vqs);
|
||||
kfree(vs->vqs);
|
||||
kvfree(vs->vqs);
|
||||
kfree(vs->old_inflight);
|
||||
kvfree(vs);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -58,9 +58,12 @@ struct vhost_vdpa {
|
|||
struct cdev cdev;
|
||||
atomic_t opened;
|
||||
u32 nvqs;
|
||||
u16 vq_num_max;
|
||||
int virtio_id;
|
||||
int minor;
|
||||
struct eventfd_ctx *config_ctx;
|
||||
/* Serialises vhost_vdpa_config_cb() against config_ctx being replaced. */
|
||||
spinlock_t config_lock;
|
||||
int in_batch;
|
||||
struct vdpa_iova_range range;
|
||||
u32 batch_asid;
|
||||
|
|
@ -194,10 +197,12 @@ static irqreturn_t vhost_vdpa_virtqueue_cb(void *private)
|
|||
static irqreturn_t vhost_vdpa_config_cb(void *private)
|
||||
{
|
||||
struct vhost_vdpa *v = private;
|
||||
struct eventfd_ctx *config_ctx = v->config_ctx;
|
||||
unsigned long flags;
|
||||
|
||||
if (config_ctx)
|
||||
eventfd_signal(config_ctx);
|
||||
spin_lock_irqsave(&v->config_lock, flags);
|
||||
if (v->config_ctx)
|
||||
eventfd_signal(v->config_ctx);
|
||||
spin_unlock_irqrestore(&v->config_lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
@ -236,7 +241,9 @@ static void vhost_vdpa_unsetup_vq_irq(struct vhost_vdpa *v, u16 qid)
|
|||
static int _compat_vdpa_reset(struct vhost_vdpa *v)
|
||||
{
|
||||
struct vdpa_device *vdpa = v->vdpa;
|
||||
const struct vdpa_config_ops *ops = vdpa->config;
|
||||
u32 flags = 0;
|
||||
int ret;
|
||||
|
||||
v->suspended = false;
|
||||
|
||||
|
|
@ -246,7 +253,14 @@ static int _compat_vdpa_reset(struct vhost_vdpa *v)
|
|||
VDPA_RESET_F_CLEAN_MAP : 0;
|
||||
}
|
||||
|
||||
return vdpa_reset(vdpa, flags);
|
||||
v->vq_num_max = 0;
|
||||
ret = vdpa_reset(vdpa, flags);
|
||||
if (!ret) {
|
||||
/* Some backends derive the max from mutable queue state. */
|
||||
v->vq_num_max = ops->get_vq_num_max(vdpa);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vhost_vdpa_reset(struct vhost_vdpa *v)
|
||||
|
|
@ -518,15 +532,22 @@ static long vhost_vdpa_get_vring_num(struct vhost_vdpa *v, u16 __user *argp)
|
|||
|
||||
static void vhost_vdpa_config_put(struct vhost_vdpa *v)
|
||||
{
|
||||
if (v->config_ctx) {
|
||||
eventfd_ctx_put(v->config_ctx);
|
||||
v->config_ctx = NULL;
|
||||
}
|
||||
struct eventfd_ctx *ctx;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&v->config_lock, flags);
|
||||
ctx = v->config_ctx;
|
||||
v->config_ctx = NULL;
|
||||
spin_unlock_irqrestore(&v->config_lock, flags);
|
||||
|
||||
if (ctx)
|
||||
eventfd_ctx_put(ctx);
|
||||
}
|
||||
|
||||
static long vhost_vdpa_set_config_call(struct vhost_vdpa *v, u32 __user *argp)
|
||||
{
|
||||
struct vdpa_callback cb;
|
||||
unsigned long flags;
|
||||
int fd;
|
||||
struct eventfd_ctx *ctx;
|
||||
|
||||
|
|
@ -536,18 +557,20 @@ static long vhost_vdpa_set_config_call(struct vhost_vdpa *v, u32 __user *argp)
|
|||
return -EFAULT;
|
||||
|
||||
ctx = fd == VHOST_FILE_UNBIND ? NULL : eventfd_ctx_fdget(fd);
|
||||
if (IS_ERR(ctx))
|
||||
return PTR_ERR(ctx);
|
||||
|
||||
spin_lock_irqsave(&v->config_lock, flags);
|
||||
swap(ctx, v->config_ctx);
|
||||
spin_unlock_irqrestore(&v->config_lock, flags);
|
||||
|
||||
if (!IS_ERR_OR_NULL(ctx))
|
||||
/*
|
||||
* The callback can no longer reach the old context, so this is the
|
||||
* last reference to it.
|
||||
*/
|
||||
if (ctx)
|
||||
eventfd_ctx_put(ctx);
|
||||
|
||||
if (IS_ERR(v->config_ctx)) {
|
||||
long ret = PTR_ERR(v->config_ctx);
|
||||
|
||||
v->config_ctx = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
v->vdpa->config->set_config_cb(v->vdpa, &cb);
|
||||
|
||||
return 0;
|
||||
|
|
@ -648,9 +671,15 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
|
|||
u32 idx;
|
||||
long r;
|
||||
|
||||
r = get_user(idx, (u32 __user *)argp);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (cmd == VHOST_SET_VRING_NUM) {
|
||||
if (copy_from_user(&s, argp, sizeof(s)))
|
||||
return -EFAULT;
|
||||
idx = s.index;
|
||||
} else {
|
||||
r = get_user(idx, (u32 __user *)argp);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (idx >= v->nvqs)
|
||||
return -ENOBUFS;
|
||||
|
|
@ -659,6 +688,23 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
|
|||
vq = &v->vqs[idx];
|
||||
|
||||
switch (cmd) {
|
||||
case VHOST_SET_VRING_NUM:
|
||||
mutex_lock(&vq->mutex);
|
||||
if (vq->private_data) {
|
||||
r = -EBUSY;
|
||||
} else if (!s.num || s.num > 0xffff ||
|
||||
s.num > v->vq_num_max ||
|
||||
(s.num & (s.num - 1))) {
|
||||
r = -EINVAL;
|
||||
} else {
|
||||
vq->num = s.num;
|
||||
r = 0;
|
||||
}
|
||||
mutex_unlock(&vq->mutex);
|
||||
if (r)
|
||||
return r;
|
||||
ops->set_vq_num(vdpa, idx, s.num);
|
||||
return 0;
|
||||
case VHOST_VDPA_SET_VRING_ENABLE:
|
||||
if (copy_from_user(&s, argp, sizeof(s)))
|
||||
return -EFAULT;
|
||||
|
|
@ -772,9 +818,6 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
|
|||
ops->set_vq_cb(vdpa, idx, &cb);
|
||||
break;
|
||||
|
||||
case VHOST_SET_VRING_NUM:
|
||||
ops->set_vq_num(vdpa, idx, vq->num);
|
||||
break;
|
||||
}
|
||||
|
||||
return r;
|
||||
|
|
@ -1613,6 +1656,7 @@ static int vhost_vdpa_probe(struct vdpa_device *vdpa)
|
|||
}
|
||||
|
||||
atomic_set(&v->opened, 0);
|
||||
spin_lock_init(&v->config_lock);
|
||||
v->minor = minor;
|
||||
v->vdpa = vdpa;
|
||||
v->nvqs = vdpa->nvqs;
|
||||
|
|
|
|||
|
|
@ -344,6 +344,17 @@ static void __vhost_vq_meta_reset(struct vhost_virtqueue *vq)
|
|||
vq->meta_iotlb[j] = NULL;
|
||||
}
|
||||
|
||||
/* Caller must hold the virtqueue mutex. */
|
||||
static void vhost_vq_invalidate_access(struct vhost_virtqueue *vq)
|
||||
{
|
||||
vq->desc = NULL;
|
||||
vq->avail = NULL;
|
||||
vq->used = NULL;
|
||||
vq->log_used = false;
|
||||
vq->log_addr = -1ull;
|
||||
__vhost_vq_meta_reset(vq);
|
||||
}
|
||||
|
||||
static void vhost_vq_meta_reset(struct vhost_dev *d)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -392,6 +403,7 @@ static void vhost_vq_reset(struct vhost_dev *dev,
|
|||
vq->busyloop_timeout = 0;
|
||||
vq->umem = NULL;
|
||||
vq->iotlb = NULL;
|
||||
vq->iotlb_miss = NULL;
|
||||
rcu_assign_pointer(vq->worker, NULL);
|
||||
vhost_vring_call_reset(&vq->call_ctx);
|
||||
__vhost_vq_meta_reset(vq);
|
||||
|
|
@ -1180,6 +1192,21 @@ void vhost_dev_stop(struct vhost_dev *dev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(vhost_dev_stop);
|
||||
|
||||
static void vhost_free_msg_locked(struct vhost_msg_node *node)
|
||||
{
|
||||
if (node->vq->iotlb_miss == node)
|
||||
node->vq->iotlb_miss = NULL;
|
||||
kfree(node);
|
||||
}
|
||||
|
||||
static void vhost_free_msg(struct vhost_dev *dev,
|
||||
struct vhost_msg_node *node)
|
||||
{
|
||||
spin_lock(&dev->iotlb_lock);
|
||||
vhost_free_msg_locked(node);
|
||||
spin_unlock(&dev->iotlb_lock);
|
||||
}
|
||||
|
||||
void vhost_clear_msg(struct vhost_dev *dev)
|
||||
{
|
||||
struct vhost_msg_node *node, *n;
|
||||
|
|
@ -1188,12 +1215,12 @@ void vhost_clear_msg(struct vhost_dev *dev)
|
|||
|
||||
list_for_each_entry_safe(node, n, &dev->read_list, node) {
|
||||
list_del(&node->node);
|
||||
kfree(node);
|
||||
vhost_free_msg_locked(node);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(node, n, &dev->pending_list, node) {
|
||||
list_del(&node->node);
|
||||
kfree(node);
|
||||
vhost_free_msg_locked(node);
|
||||
}
|
||||
|
||||
spin_unlock(&dev->iotlb_lock);
|
||||
|
|
@ -1602,7 +1629,7 @@ static void vhost_iotlb_notify_vq(struct vhost_dev *d,
|
|||
vq_msg->type == VHOST_IOTLB_MISS) {
|
||||
vhost_poll_queue(&node->vq->poll);
|
||||
list_del(&node->node);
|
||||
kfree(node);
|
||||
vhost_free_msg_locked(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1816,7 +1843,7 @@ ssize_t vhost_chr_read_iter(struct vhost_dev *dev, struct iov_iter *to,
|
|||
|
||||
ret = copy_to_iter(start, size, to);
|
||||
if (ret != size || msg->type != VHOST_IOTLB_MISS) {
|
||||
kfree(node);
|
||||
vhost_free_msg(dev, node);
|
||||
return ret;
|
||||
}
|
||||
vhost_enqueue_msg(dev, &dev->pending_list, node);
|
||||
|
|
@ -1848,7 +1875,19 @@ static int vhost_iotlb_miss(struct vhost_virtqueue *vq, u64 iova, int access)
|
|||
msg->iova = iova;
|
||||
msg->perm = access;
|
||||
|
||||
vhost_enqueue_msg(dev, &dev->read_list, node);
|
||||
spin_lock(&dev->iotlb_lock);
|
||||
/* VQ processing stops at the first miss until userspace resolves it. */
|
||||
if (vq->iotlb_miss) {
|
||||
spin_unlock(&dev->iotlb_lock);
|
||||
kfree(node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
vq->iotlb_miss = node;
|
||||
list_add_tail(&node->node, &dev->read_list);
|
||||
spin_unlock(&dev->iotlb_lock);
|
||||
|
||||
wake_up_interruptible_poll(&dev->wait, EPOLLIN | EPOLLRDNORM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1918,6 +1957,13 @@ int vq_meta_prefetch(struct vhost_virtqueue *vq)
|
|||
{
|
||||
unsigned int num = vq->num;
|
||||
|
||||
/*
|
||||
* vhost_vq_invalidate_access() clears all three addresses together.
|
||||
* A single zero address may be a valid GIOVA in IOTLB mode.
|
||||
*/
|
||||
if (!vq->desc && !vq->avail && !vq->used)
|
||||
return 0;
|
||||
|
||||
if (!vq->iotlb)
|
||||
return 1;
|
||||
|
||||
|
|
@ -2287,6 +2333,40 @@ long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *arg
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(vhost_vring_ioctl);
|
||||
|
||||
/* Caller must hold the device mutex. */
|
||||
void vhost_clear_device_iotlb(struct vhost_dev *d)
|
||||
{
|
||||
struct vhost_iotlb *iotlb;
|
||||
int i;
|
||||
|
||||
iotlb = d->iotlb;
|
||||
if (!iotlb)
|
||||
return;
|
||||
|
||||
vhost_dev_lock_vqs(d);
|
||||
|
||||
/*
|
||||
* vhost_dev_lock_vqs() takes all VQ mutexes in index order. Drop the
|
||||
* device-wide view while they are held, then clear each per-VQ view
|
||||
* and its cached ring access before releasing the locks. Workers
|
||||
* cannot observe a mixed address-space state during this handoff.
|
||||
*/
|
||||
d->iotlb = NULL;
|
||||
|
||||
for (i = 0; i < d->nvqs; ++i) {
|
||||
struct vhost_virtqueue *vq = d->vqs[i];
|
||||
|
||||
vq->iotlb = NULL;
|
||||
vhost_vq_invalidate_access(vq);
|
||||
}
|
||||
|
||||
vhost_dev_unlock_vqs(d);
|
||||
vhost_clear_msg(d);
|
||||
vhost_iotlb_free(iotlb);
|
||||
wake_up_interruptible_poll(&d->wait, EPOLLIN | EPOLLRDNORM);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vhost_clear_device_iotlb);
|
||||
|
||||
int vhost_init_device_iotlb(struct vhost_dev *d)
|
||||
{
|
||||
struct vhost_iotlb *niotlb, *oiotlb;
|
||||
|
|
@ -2307,7 +2387,10 @@ int vhost_init_device_iotlb(struct vhost_dev *d)
|
|||
|
||||
mutex_lock(&vq->mutex);
|
||||
vq->iotlb = niotlb;
|
||||
__vhost_vq_meta_reset(vq);
|
||||
if (oiotlb)
|
||||
__vhost_vq_meta_reset(vq);
|
||||
else
|
||||
vhost_vq_invalidate_access(vq);
|
||||
mutex_unlock(&vq->mutex);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ struct vhost_work {
|
|||
|
||||
struct vhost_worker;
|
||||
struct vhost_dev;
|
||||
struct vhost_msg_node;
|
||||
|
||||
struct vhost_worker_ops {
|
||||
int (*create)(struct vhost_worker *worker, struct vhost_dev *dev,
|
||||
|
|
@ -148,6 +149,8 @@ struct vhost_virtqueue {
|
|||
/* Protected by virtqueue mutex. */
|
||||
struct vhost_iotlb *umem;
|
||||
struct vhost_iotlb *iotlb;
|
||||
/* Protected by dev->iotlb_lock. */
|
||||
struct vhost_msg_node *iotlb_miss;
|
||||
void *private_data;
|
||||
VIRTIO_DECLARE_FEATURES(acked_features);
|
||||
u64 acked_backend_features;
|
||||
|
|
@ -277,6 +280,7 @@ ssize_t vhost_chr_read_iter(struct vhost_dev *dev, struct iov_iter *to,
|
|||
int noblock);
|
||||
ssize_t vhost_chr_write_iter(struct vhost_dev *dev,
|
||||
struct iov_iter *from);
|
||||
void vhost_clear_device_iotlb(struct vhost_dev *d);
|
||||
int vhost_init_device_iotlb(struct vhost_dev *d);
|
||||
|
||||
void vhost_iotlb_map_free(struct vhost_iotlb *iotlb,
|
||||
|
|
|
|||
|
|
@ -868,6 +868,8 @@ static int vhost_vsock_set_features(struct vhost_vsock *vsock, u64 features)
|
|||
if ((features & (1ULL << VIRTIO_F_ACCESS_PLATFORM))) {
|
||||
if (vhost_init_device_iotlb(&vsock->dev))
|
||||
goto err;
|
||||
} else {
|
||||
vhost_clear_device_iotlb(&vsock->dev);
|
||||
}
|
||||
|
||||
vsock->seqpacket_allow = features & (1ULL << VIRTIO_VSOCK_F_SEQPACKET);
|
||||
|
|
|
|||
|
|
@ -604,8 +604,8 @@ void unregister_virtio_device(struct virtio_device *dev)
|
|||
{
|
||||
int index = dev->index; /* save for after device release */
|
||||
|
||||
device_unregister(&dev->dev);
|
||||
virtio_debug_device_exit(dev);
|
||||
device_unregister(&dev->dev);
|
||||
ida_free(&virtio_index_ida, index);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_virtio_device);
|
||||
|
|
|
|||
|
|
@ -49,9 +49,12 @@ static void virtinput_recv_events(struct virtqueue *vq)
|
|||
le16_to_cpu(event->code),
|
||||
le32_to_cpu(event->value));
|
||||
spin_lock_irqsave(&vi->lock, flags);
|
||||
if (!vi->ready)
|
||||
continue;
|
||||
virtinput_queue_evtbuf(vi, event);
|
||||
}
|
||||
virtqueue_kick(vq);
|
||||
if (vi->ready)
|
||||
virtqueue_kick(vq);
|
||||
}
|
||||
spin_unlock_irqrestore(&vi->lock, flags);
|
||||
}
|
||||
|
|
@ -331,6 +334,7 @@ static int virtinput_probe(struct virtio_device *vdev)
|
|||
spin_lock_irqsave(&vi->lock, flags);
|
||||
vi->ready = false;
|
||||
spin_unlock_irqrestore(&vi->lock, flags);
|
||||
virtio_reset_device(vdev);
|
||||
err_mt_init_slots:
|
||||
input_free_device(vi->idev);
|
||||
err_input_alloc:
|
||||
|
|
@ -350,8 +354,9 @@ static void virtinput_remove(struct virtio_device *vdev)
|
|||
vi->ready = false;
|
||||
spin_unlock_irqrestore(&vi->lock, flags);
|
||||
|
||||
input_unregister_device(vi->idev);
|
||||
/* Callbacks use vi->idev. */
|
||||
virtio_reset_device(vdev);
|
||||
input_unregister_device(vi->idev);
|
||||
while ((buf = virtqueue_detach_unused_buf(vi->sts)) != NULL)
|
||||
kfree(buf);
|
||||
vdev->config->del_vqs(vdev);
|
||||
|
|
|
|||
|
|
@ -88,6 +88,9 @@ struct virtio_mmio_device {
|
|||
|
||||
void __iomem *base;
|
||||
unsigned long version;
|
||||
|
||||
/* True if enable_irq_wake() succeeded for the shared IRQ. */
|
||||
bool wake_irq_enabled;
|
||||
};
|
||||
|
||||
/* Configuration interface */
|
||||
|
|
@ -336,11 +339,17 @@ static void vm_del_vqs(struct virtio_device *vdev)
|
|||
{
|
||||
struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
|
||||
struct virtqueue *vq, *n;
|
||||
int irq = platform_get_irq(vm_dev->pdev, 0);
|
||||
|
||||
list_for_each_entry_safe(vq, n, &vdev->vqs, list)
|
||||
vm_del_vq(vq);
|
||||
|
||||
free_irq(platform_get_irq(vm_dev->pdev, 0), vm_dev);
|
||||
if (vm_dev->wake_irq_enabled) {
|
||||
disable_irq_wake(irq);
|
||||
vm_dev->wake_irq_enabled = false;
|
||||
}
|
||||
|
||||
free_irq(irq, vm_dev);
|
||||
}
|
||||
|
||||
static void vm_synchronize_cbs(struct virtio_device *vdev)
|
||||
|
|
@ -467,8 +476,9 @@ static int vm_find_vqs(struct virtio_device *vdev, unsigned int nvqs,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
if (of_property_read_bool(vm_dev->pdev->dev.of_node, "wakeup-source"))
|
||||
enable_irq_wake(irq);
|
||||
if (of_property_read_bool(vm_dev->pdev->dev.of_node, "wakeup-source") &&
|
||||
!enable_irq_wake(irq))
|
||||
vm_dev->wake_irq_enabled = true;
|
||||
|
||||
for (i = 0; i < nvqs; ++i) {
|
||||
struct virtqueue_info *vqi = &vqs_info[i];
|
||||
|
|
|
|||
|
|
@ -120,7 +120,9 @@ static irqreturn_t vp_interrupt(int irq, void *opaque)
|
|||
if (isr & VIRTIO_PCI_ISR_CONFIG)
|
||||
vp_config_changed(irq, opaque);
|
||||
|
||||
return vp_vring_interrupt(irq, opaque);
|
||||
vp_vring_interrupt(irq, opaque);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors,
|
||||
|
|
|
|||
|
|
@ -1670,7 +1670,7 @@ static inline int virtqueue_add_packed(struct vring_virtqueue *vq,
|
|||
struct scatterlist *sg;
|
||||
unsigned int i, n, c, descs_used, err_idx, len;
|
||||
__le16 head_flags, flags;
|
||||
u16 head, id, prev, curr, avail_used_flags;
|
||||
u16 head, id, prev, curr, avail_used_flags, unpub_flags;
|
||||
int err;
|
||||
|
||||
START_USE(vq);
|
||||
|
|
@ -1798,15 +1798,30 @@ static inline int virtqueue_add_packed(struct vring_virtqueue *vq,
|
|||
curr = vq->free_head;
|
||||
|
||||
vq->packed.avail_used_flags = avail_used_flags;
|
||||
unpub_flags = avail_used_flags ^ (1 << VRING_PACKED_DESC_F_AVAIL |
|
||||
1 << VRING_PACKED_DESC_F_USED);
|
||||
|
||||
for (n = 0; n < total_sg; n++) {
|
||||
if (i == err_idx)
|
||||
break;
|
||||
/*
|
||||
* The mapping loop made every descriptor but the head
|
||||
* available. Stamp the previous wrap counter's AVAIL and USED
|
||||
* bits on those, so that a later and shorter chain at this head
|
||||
* does not leave one of them available beyond its own last
|
||||
* descriptor. Marking them used instead would hand
|
||||
* is_used_desc_packed() a completion we never made.
|
||||
*/
|
||||
if (i != head)
|
||||
desc[i].flags = cpu_to_le16(unpub_flags);
|
||||
vring_unmap_extra_packed(vq, &vq->packed.desc_extra[curr]);
|
||||
curr = vq->packed.desc_extra[curr].next;
|
||||
i++;
|
||||
if (i >= vq->packed.vring.num)
|
||||
if (i >= vq->packed.vring.num) {
|
||||
i = 0;
|
||||
unpub_flags ^= 1 << VRING_PACKED_DESC_F_AVAIL |
|
||||
1 << VRING_PACKED_DESC_F_USED;
|
||||
}
|
||||
}
|
||||
|
||||
END_USE(vq);
|
||||
|
|
@ -1828,7 +1843,7 @@ static inline int virtqueue_add_packed_in_order(struct vring_virtqueue *vq,
|
|||
struct scatterlist *sg;
|
||||
unsigned int i, n, sg_count, err_idx, total_in_len = 0;
|
||||
__le16 head_flags, flags;
|
||||
u16 head, avail_used_flags;
|
||||
u16 head, avail_used_flags, unpub_flags;
|
||||
bool avail_wrap_counter;
|
||||
int err;
|
||||
|
||||
|
|
@ -1955,14 +1970,29 @@ static inline int virtqueue_add_packed_in_order(struct vring_virtqueue *vq,
|
|||
i = head;
|
||||
vq->packed.avail_used_flags = avail_used_flags;
|
||||
vq->packed.avail_wrap_counter = avail_wrap_counter;
|
||||
unpub_flags = avail_used_flags ^ (1 << VRING_PACKED_DESC_F_AVAIL |
|
||||
1 << VRING_PACKED_DESC_F_USED);
|
||||
|
||||
for (n = 0; n < total_sg; n++) {
|
||||
if (i == err_idx)
|
||||
break;
|
||||
/*
|
||||
* The mapping loop made every descriptor but the head
|
||||
* available. Stamp the previous wrap counter's AVAIL and USED
|
||||
* bits on those, so that a later and shorter chain at this head
|
||||
* does not leave one of them available beyond its own last
|
||||
* descriptor. Marking them used instead would hand
|
||||
* is_used_desc_packed() a completion we never made.
|
||||
*/
|
||||
if (i != head)
|
||||
desc[i].flags = cpu_to_le16(unpub_flags);
|
||||
vring_unmap_extra_packed(vq, &vq->packed.desc_extra[i]);
|
||||
i++;
|
||||
if (i >= vq->packed.vring.num)
|
||||
if (i >= vq->packed.vring.num) {
|
||||
i = 0;
|
||||
unpub_flags ^= 1 << VRING_PACKED_DESC_F_AVAIL |
|
||||
1 << VRING_PACKED_DESC_F_USED;
|
||||
}
|
||||
}
|
||||
|
||||
END_USE(vq);
|
||||
|
|
|
|||
|
|
@ -352,7 +352,7 @@ static int virtio_vdpa_find_vqs(struct virtio_device *vdev, unsigned int nvqs,
|
|||
continue;
|
||||
}
|
||||
|
||||
vqs[i] = virtio_vdpa_setup_vq(vdev, queue_idx++, vqi->callback,
|
||||
vqs[i] = virtio_vdpa_setup_vq(vdev, queue_idx, vqi->callback,
|
||||
vqi->name, vqi->ctx);
|
||||
if (IS_ERR(vqs[i])) {
|
||||
err = PTR_ERR(vqs[i]);
|
||||
|
|
@ -360,7 +360,8 @@ static int virtio_vdpa_find_vqs(struct virtio_device *vdev, unsigned int nvqs,
|
|||
}
|
||||
|
||||
if (has_affinity)
|
||||
ops->set_vq_affinity(vdpa, i, &masks[i]);
|
||||
ops->set_vq_affinity(vdpa, queue_idx, &masks[i]);
|
||||
queue_idx++;
|
||||
}
|
||||
|
||||
cb.callback = virtio_vdpa_config_cb;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user