vhost,virtio,vdpa: bugfixes

Just a bunch of fixes all over the place.
 
 Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 
 iQFDBAABCgAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmrrL5QPHG1zdEByZWRo
 YXQuY29tAAoJECgfDbjSjVRphdEIAMnblbwjUhNUZ3u3MLkXGNlcNrDiuPdhoFvY
 RfRcmCWKDBWSu25uwOqqMhmTAqKlegeaeuemKMbaz7CYZFfatRj1B5EeCbrrqRJd
 5igTf9lE7hl6eZnLMY3z6x40cgP95zfkCcFOCN8CTv31sOdHj42uNI9B1faFnowT
 aRF6PCpYQ0qkUcVZha7BxpteqfbbD+hyQp/QydhaXV+owrTdnWryWL4mxmHRhnNs
 yHSzTZL4eoVK1voWup6PusvSa2K8mrhsj0e/e8MnRI3tvFlfka/mMcYAg2DFgrsw
 1Q/Y24qvywa8REcbVhFmVrsktYt5xCJsWhCOsl90/uQj9dQEKis=
 =XA2M
 -----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:
 "vhost, virtio, and vdpa bugfixes.

  Just a bunch of fixes all over the place"

* tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost: (38 commits)
  virtio_pci: don't take the MSI mutex under cpu_hotplug_lock
  virtio_ring: Initialize id field in packed indirect descriptors
  vhost-scsi: fix page reference leak in misaligned I/O fallback
  vduse: fix VA/PA confusion in the coherent page fault path
  hwrng: virtio - prevent use-after-free after a failed restore
  virtio: use consistent virtqueue terminology
  virtio_balloon: initialize wakeup_lock before virtqueues are set up
  MAINTAINERS: add missing virtio/vdpa/vduse/vhost headers
  vhost-vdpa: initialize state before publishing cdev
  vhost: reject backend enable without a kick file
  vhost-scsi: reject delayed virtqueue setup
  virtio_ring: use shadow flags when detaching split descriptors
  vhost-scsi: validate event queue descriptor direction
  vhost-scsi: do not count PI bytes in the data length
  vdpa/snet: Don't discard the DPU error in snet_send_ctrl_msg()
  iommu/virtio: Reset device before deleting virtqueues on probe failure
  vhost: fix used ring index wrap in vhost_add_used_n_in_order
  vhost-scsi: zero misaligned I/O bounce pages
  vringh: reject empty / undersized indirect descriptor tables
  virtio_blk: Reset device on probe error paths after DRIVER_OK
  ...
This commit is contained in:
Linus Torvalds 2026-11-05 15:10:52 -08:00
commit 69e2b55fc3
22 changed files with 347 additions and 98 deletions

View File

@ -29662,9 +29662,12 @@ F: drivers/block/virtio_blk.c
F: drivers/crypto/virtio/
F: drivers/vdpa/
F: drivers/virtio/
F: include/linux/device-id/virtio.h
F: include/linux/vdpa.h
F: include/linux/virtio*.h
F: include/linux/vringh.h
F: include/uapi/linux/vdpa.h
F: include/uapi/linux/vduse.h
F: include/uapi/linux/virtio_*.h
F: net/vmw_vsock/virtio*
F: tools/virtio/
@ -29738,6 +29741,7 @@ F: drivers/vhost/
F: include/linux/sched/vhost_task.h
F: include/linux/vhost_iotlb.h
F: include/uapi/linux/vhost.h
F: include/uapi/linux/vhost_types.h
F: kernel/vhost_task.c
VIRTIO HOST (VHOST-SCSI)

View File

@ -1022,6 +1022,30 @@ static int init_vq(struct virtio_blk *vblk)
goto out;
for (i = 0; i < num_vqs; i++) {
unsigned int ring_size = virtqueue_get_vring_size(vqs[i]);
/*
* Each request consumes 2 extra descriptors (out_hdr and
* in_hdr) in addition to the data segments.
*
* At initial probe (!vblk->disk), each virtqueue must fit at
* least 1 data segment + 2 header descriptors (3).
* On resume/recovery (vblk->disk), each virtqueue must fit the
* existing queue_max_segments() + 2 header descriptors.
*/
unsigned int min_ring_size = 3;
if (vblk->disk)
min_ring_size = queue_max_segments(vblk->disk->queue) + 2;
if (ring_size < min_ring_size) {
dev_err(&vdev->dev,
"virtqueue %u ring size %u is smaller than minimum %u\n",
i, ring_size, min_ring_size);
vdev->config->del_vqs(vdev);
err = -EINVAL;
goto out;
}
spin_lock_init(&vblk->vqs[i].lock);
vblk->vqs[i].vq = vqs[i];
}
@ -1254,7 +1278,7 @@ static int virtblk_read_limits(struct virtio_blk *vblk,
u16 min_io_size;
u8 physical_block_exp, alignment_offset;
size_t max_dma_size;
int err;
int err, i;
/* We need to know how many segments before we allocate. */
err = virtio_cread_feature(vdev, VIRTIO_BLK_F_SEG_MAX,
@ -1268,6 +1292,23 @@ static int virtblk_read_limits(struct virtio_blk *vblk,
/* Prevent integer overflows and honor max vq size */
sg_elems = min_t(u32, sg_elems, VIRTIO_BLK_MAX_SG_ELEMS - 2);
/*
* virtblk_add_req() uses separate outgoing and incoming header
* descriptors (out_hdr and in_hdr), consuming 2 extra descriptors
* per request in addition to the data segments.
*
* Per virtio specification (2.7.5.3.1), a driver MUST NOT create a
* descriptor chain longer than the Queue Size of the device.
*
* Clamp max_segments to (ring_size - 2) across all virtqueues so
* that a request never exceeds the ring size of any queue.
*/
for (i = 0; i < vblk->num_vqs; i++) {
u32 ring_size = virtqueue_get_vring_size(vblk->vqs[i].vq);
sg_elems = min_t(u32, sg_elems, ring_size - 2);
}
/* We can handle whatever the host told us to handle. */
lim->max_segments = sg_elems;
@ -1467,6 +1508,7 @@ static int virtblk_probe(struct virtio_device *vdev)
mutex_init(&vblk->vdev_mutex);
vblk->vdev = vdev;
vblk->disk = NULL;
INIT_WORK(&vblk->config_work, virtblk_config_changed_work);
@ -1548,6 +1590,7 @@ static int virtblk_probe(struct virtio_device *vdev)
return 0;
out_cleanup_disk:
virtio_reset_device(vdev);
put_disk(vblk->disk);
out_free_tags:
blk_mq_free_tag_set(&vblk->tag_set);

View File

@ -190,6 +190,7 @@ static int probe_common(struct virtio_device *vdev)
err_find:
ida_free(&rng_index_ida, index);
err_ida:
vdev->priv = NULL;
kfree(vi);
return err;
}
@ -198,6 +199,11 @@ static void remove_common(struct virtio_device *vdev)
{
struct virtrng_info *vi = vdev->priv;
if (!vi) {
virtio_reset_device(vdev);
return;
}
vi->hwrng_removed = true;
vi->data_avail = 0;
vi->data_idx = 0;
@ -207,6 +213,7 @@ static void remove_common(struct virtio_device *vdev)
virtio_reset_device(vdev);
vdev->config->del_vqs(vdev);
ida_free(&rng_index_ida, vi->index);
vdev->priv = NULL;
kfree(vi);
}

View File

@ -1227,12 +1227,12 @@ static int viommu_probe(struct virtio_device *vdev)
/* Populate the event queue with buffers */
ret = viommu_fill_evtq(viommu);
if (ret)
goto err_free_vqs;
goto err_reset_vdev;
ret = iommu_device_sysfs_add(&viommu->iommu, dev, NULL, "%s",
virtio_bus_name(vdev));
if (ret)
goto err_free_vqs;
goto err_reset_vdev;
vdev->priv = viommu;
@ -1244,6 +1244,8 @@ static int viommu_probe(struct virtio_device *vdev)
return 0;
err_reset_vdev:
virtio_reset_device(vdev);
err_free_vqs:
vdev->config->del_vqs(vdev);

View File

@ -3059,7 +3059,15 @@ static int mlx5_vdpa_change_map(struct mlx5_vdpa_dev *mvdev,
bool teardown = !is_resumable(ndev);
int err;
suspend_vqs(ndev, 0, ndev->cur_num_vqs);
if ((mvdev->status & VIRTIO_CONFIG_S_DRIVER_OK) && !mvdev->suspended) {
err = suspend_vqs(ndev, 0, ndev->cur_num_vqs);
if (err && !teardown) {
for (int i = 0; i < ndev->cur_num_vqs; i++)
resume_vqs(ndev, i, 1);
mlx5_vdpa_put_mr(mvdev, new_mr);
return err;
}
}
if (teardown) {
err = save_channels_info(ndev);
if (err) {

View File

@ -291,7 +291,6 @@ static int snet_send_ctrl_msg(struct snet *snet, u16 opcode, u16 vq_idx)
/* Check for errors */
val = snet_read_ctrl(regs);
ret = SNET_VAL_TO_ERR(val);
/* Clear the chunk ready bit */
val &= ~SNET_CTRL_CHUNK_RDY_MASK;
@ -301,6 +300,8 @@ static int snet_send_ctrl_msg(struct snet *snet, u16 opcode, u16 vq_idx)
if (ret)
SNET_WARN(pdev, "Timeout waiting for DPU to complete a control command, err %d\n",
ret);
else
ret = SNET_VAL_TO_ERR(val);
exit:
mutex_unlock(&snet->ctrl_lock);

View File

@ -1313,15 +1313,16 @@ static int vdpa_nl_cmd_dev_config_get_doit(struct sk_buff *skb, struct genl_info
}
err = vdpa_dev_config_fill(vdev, msg, info->snd_portid, info->snd_seq,
0, info->extack);
if (!err)
if (!err) {
err = genlmsg_reply(msg, info);
msg = NULL;
}
mdev_err:
put_device(dev);
dev_err:
up_read(&vdpa_dev_lock);
if (err)
nlmsg_free(msg);
nlmsg_free(msg);
return err;
}

View File

@ -638,6 +638,7 @@ static int vdpasim_set_map(struct vdpa_device *vdpa, unsigned int asid,
if (asid >= vdpasim->dev_attr.nas)
return -EINVAL;
mutex_lock(&vdpasim->mutex);
spin_lock(&vdpasim->iommu_lock);
iommu = &vdpasim->iommu[asid];
@ -652,11 +653,13 @@ static int vdpasim_set_map(struct vdpa_device *vdpa, unsigned int asid,
goto err;
}
spin_unlock(&vdpasim->iommu_lock);
mutex_unlock(&vdpasim->mutex);
return 0;
err:
vhost_iotlb_reset(iommu);
spin_unlock(&vdpasim->iommu_lock);
mutex_unlock(&vdpasim->mutex);
return ret;
}
@ -713,6 +716,7 @@ static int vdpasim_dma_map(struct vdpa_device *vdpa, unsigned int asid,
if (asid >= vdpasim->dev_attr.nas)
return -EINVAL;
mutex_lock(&vdpasim->mutex);
spin_lock(&vdpasim->iommu_lock);
if (vdpasim->iommu_pt[asid]) {
vhost_iotlb_reset(&vdpasim->iommu[asid]);
@ -721,6 +725,7 @@ static int vdpasim_dma_map(struct vdpa_device *vdpa, unsigned int asid,
ret = vhost_iotlb_add_range_ctx(&vdpasim->iommu[asid], iova,
iova + size - 1, pa, perm, opaque);
spin_unlock(&vdpasim->iommu_lock);
mutex_unlock(&vdpasim->mutex);
return ret;
}
@ -733,6 +738,7 @@ static int vdpasim_dma_unmap(struct vdpa_device *vdpa, unsigned int asid,
if (asid >= vdpasim->dev_attr.nas)
return -EINVAL;
mutex_lock(&vdpasim->mutex);
spin_lock(&vdpasim->iommu_lock);
if (vdpasim->iommu_pt[asid]) {
vhost_iotlb_reset(&vdpasim->iommu[asid]);
@ -740,6 +746,7 @@ static int vdpasim_dma_unmap(struct vdpa_device *vdpa, unsigned int asid,
}
vhost_iotlb_del_range(&vdpasim->iommu[asid], iova, iova + size - 1);
spin_unlock(&vdpasim->iommu_lock);
mutex_unlock(&vdpasim->mutex);
return 0;
}

View File

@ -76,6 +76,10 @@ int vduse_domain_set_map(struct vduse_iova_domain *domain,
for (map = vhost_iotlb_itree_first(iotlb, start, last); map;
map = vhost_iotlb_itree_next(map, start, last)) {
map_file = (struct vdpa_map_file *)map->opaque;
if (!map_file || map_file->file == domain->file) {
ret = -EINVAL;
goto err;
}
ret = vduse_iotlb_add_range(domain, map->start, map->last,
map->addr, map->perm,
map_file->file,
@ -226,6 +230,7 @@ vduse_domain_get_coherent_page(struct vduse_iova_domain *domain, u64 iova)
{
u64 start = iova & PAGE_MASK;
u64 last = start + PAGE_SIZE - 1;
struct vdpa_map_file *map_file;
struct vhost_iotlb_map *map;
struct page *page = NULL;
@ -234,6 +239,10 @@ vduse_domain_get_coherent_page(struct vduse_iova_domain *domain, u64 iova)
if (!map)
goto out;
map_file = (struct vdpa_map_file *)map->opaque;
if (!map_file || map_file->file != domain->file)
goto out;
page = pfn_to_page((map->addr + iova - map->start) >> PAGE_SHIFT);
get_page(page);
out:
@ -579,7 +588,7 @@ static int vduse_domain_mmap(struct file *file, struct vm_area_struct *vma)
{
struct vduse_iova_domain *domain = file->private_data;
vm_flags_set(vma, VM_DONTDUMP | VM_DONTEXPAND);
vm_flags_set(vma, VM_IO | VM_DONTDUMP | VM_DONTEXPAND);
vma->vm_private_data = domain;
vma->vm_ops = &vduse_domain_mmap_ops;

View File

@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/cpu.h>
#include <linux/eventfd.h>
#include <linux/slab.h>
#include <linux/wait.h>
@ -1430,14 +1431,10 @@ static void vduse_vq_update_effective_cpu(struct vduse_virtqueue *vq)
{
int curr_cpu = vq->irq_effective_cpu;
while (true) {
curr_cpu = cpumask_next(curr_cpu, &vq->irq_affinity);
if (cpu_online(curr_cpu))
break;
if (curr_cpu >= nr_cpu_ids)
curr_cpu = IRQ_UNBOUND;
}
curr_cpu = cpumask_next_and_wrap(curr_cpu, &vq->irq_affinity,
cpu_online_mask);
if (curr_cpu >= nr_cpu_ids)
curr_cpu = IRQ_UNBOUND;
vq->irq_effective_cpu = curr_cpu;
}
@ -1672,10 +1669,12 @@ static long vduse_dev_ioctl(struct file *file, unsigned int cmd,
ret = 0;
index = array_index_nospec(index, dev->vq_num);
if (!vduse_vq_signal_irqfd(dev->vqs[index])) {
cpus_read_lock();
vduse_vq_update_effective_cpu(dev->vqs[index]);
ret = vduse_dev_queue_irq_work(dev,
&dev->vqs[index]->inject,
dev->vqs[index]->irq_effective_cpu);
cpus_read_unlock();
}
break;
}

View File

@ -638,6 +638,16 @@ vhost_scsi_do_evt_work(struct vhost_scsi *vs, struct vhost_scsi_evt *evt)
return;
}
/*
* Eventq buffers are device-writable only. A nonzero in count
* guarantees that out is within the bounds of vq->iov.
*/
if (unlikely(out || !in)) {
vq_err(vq, "Invalid event buffer: out %u, in %u\n", out, in);
vs->vs_events_missed = true;
return;
}
if ((vq->iov[out].iov_len != sizeof(struct virtio_scsi_event))) {
vq_err(vq, "Expecting virtio_scsi_event, got %zu bytes\n",
vq->iov[out].iov_len);
@ -660,19 +670,26 @@ vhost_scsi_do_evt_work(struct vhost_scsi *vs, struct vhost_scsi_evt *evt)
vhost_scsi_log_write(vq, vq_log, log_num);
}
static void vhost_scsi_complete_events(struct vhost_scsi *vs, bool drop)
/* Caller must hold the event virtqueue mutex. */
static void __vhost_scsi_complete_events(struct vhost_scsi *vs, bool drop)
{
struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq;
struct vhost_scsi_evt *evt, *t;
struct llist_node *llnode;
mutex_lock(&vq->mutex);
llnode = llist_del_all(&vs->vs_event_list);
llnode = llist_reverse_order(llist_del_all(&vs->vs_event_list));
llist_for_each_entry_safe(evt, t, llnode, list) {
if (!drop)
vhost_scsi_do_evt_work(vs, evt);
vhost_scsi_free_evt(vs, evt);
}
}
static void vhost_scsi_complete_events(struct vhost_scsi *vs, bool drop)
{
struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq;
mutex_lock(&vq->mutex);
__vhost_scsi_complete_events(vs, drop);
mutex_unlock(&vq->mutex);
}
@ -883,11 +900,16 @@ vhost_scsi_map_to_sgl(struct vhost_scsi_cmd *cmd,
vhost_scsi_revert_map_iov_to_sgl(iter, *sgl, sg);
iov_iter_revert(iter, bytes);
/*
* pages[npages] is the first page not added to the SG list.
* The remaining data starts at offset in that page.
*/
while (bytes) {
n = min_t(unsigned int, PAGE_SIZE, bytes);
n = min_t(unsigned int, PAGE_SIZE - offset, bytes);
put_page(pages[npages++]);
bytes -= n;
offset = 0;
}
return -EINVAL;
@ -937,7 +959,12 @@ vhost_scsi_copy_iov_to_sgl(struct vhost_scsi_cmd *cmd, struct iov_iter *iter,
}
for_each_sgtable_sg(sg_table, sg, i) {
page = alloc_page(GFP_KERNEL);
/*
* Completion copies the full bounce buffer, including bytes the
* target did not write. This is already the slow fallback for
* misaligned I/O, so zeroing is acceptable here.
*/
page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (!page) {
ret = -ENOMEM;
goto err;
@ -1534,9 +1561,17 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
* vhost_scsi_queue_data_in() and vhost_scsi_queue_status()
*/
cmd->tvc_vq_desc = vc.head;
/*
* vhost_scsi_mapal() maps the PI bytes into cmd->prot_table.
* It maps the remaining exp_data_len bytes into cmd->table,
* which is the data SGL passed to the target core. Its
* expected length must therefore exclude prot_bytes.
*
* sbc_check_prot() derives the data length for protected READ
* and WRITE commands from the CDB.
*/
vhost_scsi_target_queue_cmd(nexus, cmd, cdb, lun, task_attr,
data_direction,
exp_data_len + prot_bytes);
data_direction, exp_data_len);
ret = 0;
err:
/*
@ -1859,7 +1894,7 @@ vhost_scsi_send_evt(struct vhost_scsi *vs, struct vhost_virtqueue *vq,
llist_add(&evt->list, &vs->vs_event_list);
if (!vhost_vq_work_queue(vq, &vs->vs_event_work))
vhost_scsi_complete_events(vs, true);
__vhost_scsi_complete_events(vs, true);
}
static void vhost_scsi_evt_handle_kick(struct vhost_work *work)
@ -2105,6 +2140,9 @@ vhost_scsi_set_endpoint(struct vhost_scsi *vs,
for (i = 0; i < vs->dev.nvqs; i++) {
vq = &vs->vqs[i].vq;
if (i >= VHOST_SCSI_VQ_IO && !vs->vqs[i].scsi_cmds)
continue;
mutex_lock(&vq->mutex);
vhost_vq_set_backend(vq, vs_tpg);
vhost_vq_init_access(vq);
@ -2466,10 +2504,16 @@ vhost_scsi_ioctl(struct file *f,
default:
mutex_lock(&vs->dev.mutex);
r = vhost_dev_ioctl(&vs->dev, ioctl, argp);
if (r == -ENOIOCTLCMD)
r = vhost_vring_ioctl(&vs->dev, ioctl, argp);
else
if (r == -ENOIOCTLCMD) {
if (vs->vs_tpg &&
(ioctl == VHOST_SET_VRING_NUM ||
ioctl == VHOST_SET_VRING_ADDR))
r = -EBUSY;
else
r = vhost_vring_ioctl(&vs->dev, ioctl, argp);
} else {
vhost_scsi_flush(vs);
}
mutex_unlock(&vs->dev.mutex);
return r;
}

View File

@ -287,6 +287,10 @@ static long vhost_test_set_backend(struct vhost_test *n, unsigned index, int fd)
r = -EFAULT;
goto err_vq;
}
if (enable && !vq->kick) {
r = -EINVAL;
goto err_vq;
}
if (!enable) {
vhost_poll_stop(&vq->poll);
backend = vhost_vq_get_backend(vq);

View File

@ -767,13 +767,46 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
if (ops->get_status(vdpa) &
VIRTIO_CONFIG_S_DRIVER_OK)
vhost_vdpa_unsetup_vq_irq(v, idx);
/*
* The parent caches call_ctx.ctx in cb.trigger without
* holding a reference, so it has to stop using it
* before vhost_vring_ioctl() drops the last one.
*/
cb.callback = NULL;
cb.private = NULL;
cb.trigger = NULL;
ops->set_vq_cb(vdpa, idx, &cb);
/*
* A parent interrupt handler that loaded the callback
* before that store can still be running, so wait for
* it to finish with the context too.
*/
if (ops->get_vq_irq) {
int irq = ops->get_vq_irq(vdpa, idx);
if (irq >= 0)
synchronize_irq(irq);
}
}
break;
}
r = vhost_vring_ioctl(&v->vdev, cmd, argp);
if (r)
if (r) {
/*
* A failure here means the swap never happened and the old
* context is still installed, so give the parent back the
* callback that was torn down above.
*/
if (cmd == VHOST_SET_VRING_CALL && vq->call_ctx.ctx) {
cb.callback = vhost_vdpa_virtqueue_cb;
cb.private = vq;
cb.trigger = vq->call_ctx.ctx;
ops->set_vq_cb(vdpa, idx, &cb);
}
return r;
}
switch (cmd) {
case VHOST_SET_VRING_ADDR:
@ -972,20 +1005,14 @@ static void vhost_vdpa_pa_unmap(struct vhost_vdpa *v, struct vhost_iotlb *iotlb,
{
struct vhost_dev *dev = &v->vdev;
struct vhost_iotlb_map *map;
struct page *page;
unsigned long pfn, pinned;
while ((map = vhost_iotlb_itree_first(iotlb, start, last)) != NULL) {
pinned = PFN_DOWN(map->size);
for (pfn = PFN_DOWN(map->addr);
pinned > 0; pfn++, pinned--) {
page = pfn_to_page(pfn);
if (map->perm & VHOST_ACCESS_WO)
set_page_dirty_lock(page);
unpin_user_page(page);
}
atomic64_sub(PFN_DOWN(map->size), &dev->mm->pinned_vm);
/* Stop device access before releasing the pinned pages. */
vhost_vdpa_general_unmap(v, map, asid);
unpin_user_page_range_dirty_lock(pfn_to_page(PFN_DOWN(map->addr)),
PFN_DOWN(map->size),
map->perm & VHOST_ACCESS_WO);
atomic64_sub(PFN_DOWN(map->size), &dev->mm->pinned_vm);
vhost_iotlb_map_free(iotlb, map);
}
}
@ -997,10 +1024,10 @@ static void vhost_vdpa_va_unmap(struct vhost_vdpa *v, struct vhost_iotlb *iotlb,
struct vdpa_map_file *map_file;
while ((map = vhost_iotlb_itree_first(iotlb, start, last)) != NULL) {
vhost_vdpa_general_unmap(v, map, asid);
map_file = (struct vdpa_map_file *)map->opaque;
fput(map_file->file);
kfree(map_file);
vhost_vdpa_general_unmap(v, map, asid);
vhost_iotlb_map_free(iotlb, map);
}
}
@ -1074,21 +1101,39 @@ static int vhost_vdpa_map(struct vhost_vdpa *v, struct vhost_iotlb *iotlb,
return 0;
}
static void vhost_vdpa_unmap(struct vhost_vdpa *v,
struct vhost_iotlb *iotlb,
u64 iova, u64 size)
static int vhost_vdpa_unmap(struct vhost_vdpa *v,
struct vhost_iotlb *iotlb,
u64 iova, u64 size)
{
struct vdpa_device *vdpa = v->vdpa;
const struct vdpa_config_ops *ops = vdpa->config;
struct vhost_iotlb_map *map;
struct vhost_iotlb new_iotlb;
u32 asid = iotlb_to_asid(iotlb);
u64 last = iova + size - 1;
int ret;
vhost_vdpa_iotlb_unmap(v, iotlb, iova, iova + size - 1, asid);
if (ops->set_map && !ops->dma_map) {
vhost_iotlb_init(&new_iotlb, iotlb->limit, iotlb->flags);
list_for_each_entry(map, &iotlb->list, link) {
if (map->start <= last && map->last >= iova)
continue;
ret = vhost_iotlb_add_range_ctx(&new_iotlb, map->start,
map->last, map->addr,
map->perm, map->opaque);
if (ret)
goto reset;
}
if (ops->set_map) {
if (!v->in_batch)
ops->set_map(vdpa, asid, iotlb);
ret = ops->set_map(vdpa, asid, &new_iotlb);
reset:
vhost_iotlb_reset(&new_iotlb);
if (ret)
return ret;
}
vhost_vdpa_iotlb_unmap(v, iotlb, iova, last, asid);
return 0;
}
static int vhost_vdpa_va_map(struct vhost_vdpa *v,
@ -1151,7 +1196,7 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
unsigned long list_size = PAGE_SIZE / sizeof(struct page *);
unsigned int gup_flags = FOLL_LONGTERM;
unsigned long npages, cur_base, map_pfn, last_pfn = 0;
unsigned long lock_limit, sz2pin, nchunks, i;
unsigned long lock_limit, sz2pin, i;
unsigned long page_offset;
u64 start = iova;
long pinned;
@ -1187,7 +1232,6 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
cur_base = uaddr & PAGE_MASK;
iova &= PAGE_MASK;
nchunks = 0;
while (npages) {
sz2pin = min_t(unsigned long, npages, list_size);
@ -1202,7 +1246,6 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
}
goto out;
}
nchunks++;
if (!last_pfn)
map_pfn = page_to_pfn(page_list[0]);
@ -1233,7 +1276,6 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
map_pfn = this_pfn;
iova += csize;
nchunks = 0;
}
last_pfn = this_pfn;
@ -1248,9 +1290,7 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
PFN_PHYS(map_pfn), perm, NULL);
out:
if (ret) {
if (nchunks) {
unsigned long pfn;
if (last_pfn) {
/*
* Unpin the outstanding pages which are yet to be
* mapped but haven't due to vdpa_map() or
@ -1260,9 +1300,9 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
* the corresponding unpinning will be handled by
* vdpa_unmap().
*/
WARN_ON(!last_pfn);
for (pfn = map_pfn; pfn <= last_pfn; pfn++)
unpin_user_page(pfn_to_page(pfn));
unpin_user_page_range_dirty_lock(pfn_to_page(map_pfn),
last_pfn - map_pfn + 1,
false);
}
vhost_vdpa_unmap(v, iotlb, start, size);
}
@ -1342,7 +1382,11 @@ static int vhost_vdpa_process_iotlb_msg(struct vhost_dev *dev, u32 asid,
r = vhost_vdpa_process_iotlb_update(v, iotlb, msg);
break;
case VHOST_IOTLB_INVALIDATE:
vhost_vdpa_unmap(v, iotlb, msg->iova, msg->size);
if (!msg->size || msg->iova > U64_MAX - msg->size + 1) {
r = -EINVAL;
break;
}
r = vhost_vdpa_unmap(v, iotlb, msg->iova, msg->size);
break;
case VHOST_IOTLB_BATCH_BEGIN:
v->batch_asid = asid;
@ -1655,7 +1699,7 @@ static int vhost_vdpa_probe(struct vdpa_device *vdpa)
return minor;
}
atomic_set(&v->opened, 0);
atomic_set(&v->opened, 1);
spin_lock_init(&v->config_lock);
v->minor = minor;
v->vdpa = vdpa;
@ -1678,16 +1722,17 @@ static int vhost_vdpa_probe(struct vdpa_device *vdpa)
cdev_init(&v->cdev, &vhost_vdpa_fops);
v->cdev.owner = THIS_MODULE;
init_completion(&v->completion);
for (i = 0; i < VHOST_VDPA_IOTLB_BUCKETS; i++)
INIT_HLIST_HEAD(&v->as[i]);
r = cdev_device_add(&v->cdev, &v->dev);
if (r)
goto err;
init_completion(&v->completion);
vdpa_set_drvdata(vdpa, v);
for (i = 0; i < VHOST_VDPA_IOTLB_BUCKETS; i++)
INIT_HLIST_HEAD(&v->as[i]);
atomic_set_release(&v->opened, 0);
return 0;

View File

@ -1766,7 +1766,8 @@ ssize_t vhost_chr_write_iter(struct vhost_dev *dev,
goto done;
}
if (msg.type == VHOST_IOTLB_UPDATE && msg.size == 0) {
if (msg.type == VHOST_IOTLB_UPDATE &&
(!msg.size || !msg.perm || (msg.perm & ~VHOST_ACCESS_RW))) {
ret = -EINVAL;
goto done;
}
@ -3179,7 +3180,6 @@ static int vhost_add_used_n_in_order(struct vhost_virtqueue *vq,
return -EINVAL;
start = vq->last_used_idx & (vq->num - 1);
used = vq->used->ring + start;
for (i = 0; i < count; i++) {
if (vhost_put_used(vq, &heads[i], start, 1)) {
@ -3188,19 +3188,35 @@ static int vhost_add_used_n_in_order(struct vhost_virtqueue *vq,
}
start += nheads[i];
new += nheads[i];
if (start >= vq->num)
start -= vq->num;
start &= vq->num - 1;
}
if (unlikely(vq->log_used)) {
unsigned int log_count = 0;
int log_start = 0, next;
/* Make sure data is seen before log. */
smp_wmb();
/* Log used ring entry write. */
log_used(vq, ((void __user *)used - (void __user *)vq->used),
(vq->num - start) * sizeof *used);
if (start + count > vq->num)
log_used(vq, 0,
(start + count - vq->num) * sizeof *used);
/* Log contiguous runs of used ring entries that were written. */
start = vq->last_used_idx & (vq->num - 1);
for (i = 0; i < count; i++) {
if (!log_count)
log_start = start;
log_count++;
next = start + nheads[i];
if (next >= vq->num)
next -= vq->num;
if (i + 1 == count || next != start + 1) {
used = vq->used->ring + log_start;
log_used(vq,
((void __user *)used -
(void __user *)vq->used),
log_count * sizeof(*used));
log_count = 0;
}
start = next;
}
}
old = vq->last_used_idx;

View File

@ -197,8 +197,9 @@ static int move_to_indirect(const struct vringh *vrh,
}
len = vringh32_to_cpu(vrh, desc->len);
if (unlikely(len % sizeof(struct vring_desc))) {
vringh_bad("Strange indirect len %u", desc->len);
if (unlikely(len < sizeof(struct vring_desc) ||
len % sizeof(struct vring_desc))) {
vringh_bad("Invalid indirect len %u", desc->len);
return -EINVAL;
}
@ -333,6 +334,17 @@ __vringh_iov(struct vringh *vrh, u16 i,
if (unlikely(err))
goto fail;
if (up_next == -1)
count++;
else
indirect_count++;
if (count > vrh->vring.num || indirect_count > desc_max) {
vringh_bad("Descriptor loop in %p", descs);
err = -ELOOP;
goto fail;
}
if (unlikely(desc.flags &
cpu_to_vringh16(vrh, VRING_DESC_F_INDIRECT))) {
u64 a = vringh64_to_cpu(vrh, desc.addr);
@ -358,17 +370,6 @@ __vringh_iov(struct vringh *vrh, u16 i,
continue;
}
if (up_next == -1)
count++;
else
indirect_count++;
if (count > vrh->vring.num || indirect_count > desc_max) {
vringh_bad("Descriptor loop in %p", descs);
err = -ELOOP;
goto fail;
}
if (desc.flags & cpu_to_vringh16(vrh, VRING_DESC_F_WRITE))
iov = wiov;
else {
@ -1244,7 +1245,7 @@ static inline int putu16_iotlb(const struct vringh *vrh,
/* Atomic write is needed for putu16 */
ret = iotlb_translate(vrh, (u64)(uintptr_t)p, sizeof(*p),
NULL, &ivec, VHOST_MAP_RO);
NULL, &ivec, VHOST_MAP_WO);
if (ret < 0)
return ret;

View File

@ -942,6 +942,7 @@ static int virtballoon_probe(struct virtio_device *vdev)
INIT_WORK(&vb->update_balloon_stats_work, update_balloon_stats_func);
INIT_WORK(&vb->update_balloon_size_work, update_balloon_size_func);
spin_lock_init(&vb->stop_update_lock);
spin_lock_init(&vb->wakeup_lock);
mutex_init(&vb->balloon_lock);
init_waitqueue_head(&vb->acked);
vb->vdev = vdev;
@ -1049,8 +1050,6 @@ static int virtballoon_probe(struct virtio_device *vdev)
goto out_unregister_oom;
}
spin_lock_init(&vb->wakeup_lock);
/*
* The virtio balloon itself can't wake up the device, but it is
* responsible for processing wakeup events passed up from the transport

View File

@ -270,7 +270,7 @@ static void vm_reset(struct virtio_device *vdev)
/* Transport interface */
/* the notify function used when creating a virt queue */
/* the notify function used when creating a virtqueue */
static bool vm_notify(struct virtqueue *vq)
{
struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev);

View File

@ -47,7 +47,7 @@ void vp_synchronize_vectors(struct virtio_device *vdev)
synchronize_irq(pci_irq_vector(vp_dev->pci_dev, i));
}
/* the notify function used when creating a virt queue */
/* the notify function used when creating a virtqueue */
bool vp_notify(struct virtqueue *vq)
{
/* we write the queue's selector into the notification register to
@ -224,6 +224,12 @@ static struct virtqueue *vp_setup_vq(struct virtio_device *vdev, unsigned int in
goto out_info;
info->vq = vq;
if (msix_vec != VIRTIO_MSI_NO_VECTOR)
info->virq = pci_irq_vector(vp_dev->pci_dev, msix_vec);
else
info->virq = 0;
if (callback) {
spin_lock_irqsave(&vp_dev->lock, flags);
if (!vp_is_slow_path_vector(msix_vec))
@ -571,7 +577,7 @@ int vp_set_vq_affinity(struct virtqueue *vq, const struct cpumask *cpu_mask)
if (vp_dev->msix_enabled) {
mask = vp_dev->msix_affinity_masks[info->msix_vector];
irq = pci_irq_vector(vp_dev->pci_dev, info->msix_vector);
irq = info->virq;
if (!cpu_mask)
irq_update_affinity_hint(irq, NULL);
else {
@ -633,7 +639,7 @@ static bool vp_supports_pm_no_reset(struct device *dev)
pci_read_config_word(pci_dev, pci_dev->pm_cap + PCI_PM_CTRL, &pmcsr);
if (PCI_POSSIBLE_ERROR(pmcsr)) {
dev_err(dev, "Unable to query pmcsr");
dev_err(dev, "Unable to query pmcsr\n");
return false;
}

View File

@ -40,6 +40,9 @@ struct virtio_pci_vq_info {
/* MSI-X vector (or none) */
unsigned int msix_vector;
/* Linux IRQ number of msix_vector, or 0 if not mapped */
unsigned int virq;
};
struct virtio_pci_admin_vq {
@ -127,7 +130,7 @@ static struct virtio_pci_device *to_vp_device(struct virtio_device *vdev)
/* wait for pending irq handlers */
void vp_synchronize_vectors(struct virtio_device *vdev);
/* the notify function used when creating a virt queue */
/* the notify function used when creating a virtqueue */
bool vp_notify(struct virtqueue *vq);
/* the config->del_vqs() implementation */
void vp_del_vqs(struct virtio_device *vdev);

View File

@ -559,6 +559,11 @@ static void virtqueue_init(struct vring_virtqueue *vq, u32 num)
vq->in_use = false;
vq->last_add_time_valid = false;
#endif
if (virtqueue_is_in_order(vq)) {
vq->free_head = 0;
vq->batch_last.id = UINT_MAX;
}
}
@ -905,7 +910,6 @@ static unsigned detach_buf_split_in_order(struct vring_virtqueue *vq,
{
struct vring_desc_extra *extra;
unsigned int i;
__virtio16 nextflag = cpu_to_virtio16(vq->vq.vdev, VRING_DESC_F_NEXT);
/* Clear data ptr. */
vq->split.desc_state[head].data = NULL;
@ -915,7 +919,7 @@ static unsigned detach_buf_split_in_order(struct vring_virtqueue *vq,
/* Put back on free list: unmap first-level descriptors and find end */
i = head;
while (vq->split.vring.desc[i].flags & nextflag) {
while (extra[i].flags & VRING_DESC_F_NEXT) {
i = vring_unmap_one_split(vq, &extra[i]);
vq->vq.num_free++;
}
@ -1571,6 +1575,11 @@ static int virtqueue_add_indirect_packed(struct vring_virtqueue *vq,
0 : VRING_DESC_F_WRITE);
desc[i].addr = cpu_to_le64(addr);
desc[i].len = cpu_to_le32(len);
/* The id field is unused in indirect descriptors but
* must be initialized to prevent leaking kernel heap
* memory to the device through the DMA-mapped table.
*/
desc[i].id = 0;
if (unlikely(vq->use_map_api)) {
extra[i].addr = premapped ? DMA_MAPPING_ERROR : addr;

View File

@ -73,7 +73,7 @@
* @max_cs_hold_ns: the maximum delay supported before chipselect is deasserted,
* in ns unit, 0 means delay is not supported to introduce before chipselect
* is deasserted.
* @max_cs_incative_ns: maximum delay supported after chipselect is deasserted,
* @max_cs_inactive_ns: maximum delay supported after chipselect is deasserted,
* in ns unit, 0 means delay is not supported to introduce after chipselect is
* deasserted.
*/

View File

@ -458,6 +458,8 @@ int main(int argc, char *argv[])
int err;
unsigned i;
void *ret;
struct vring_desc *ind;
char *data;
bool (*getrange)(struct vringh *vrh, u64 addr, struct vringh_range *r);
bool fast_vringh = false, parallel = false;
@ -755,6 +757,45 @@ int main(int argc, char *argv[])
vringh_iov_cleanup(&riov);
}
/*
* Regression test: a top-level indirect descriptor whose NEXT
* points back to itself must be rejected with -ELOOP instead of
* looping forever. Use index 1 rather than 0 so that returning
* from the indirect table re-enters the same top-level descriptor.
*/
ind = __user_addr_max - USER_MEM/2;
data = __user_addr_max - USER_MEM/4;
/* Fresh ring and host state; resets last_avail_idx to 0. */
vring_init(&vrh.vring, RINGSIZE, __user_addr_min, ALIGN);
vringh_init_user(&vrh, vdev.features, RINGSIZE, true,
vrh.vring.desc, vrh.vring.avail, vrh.vring.used);
/* Single-entry indirect table pointing at valid data. */
ind[0].addr = (unsigned long)data;
ind[0].len = 1;
ind[0].flags = 0;
/* Top-level desc[1]: INDIRECT, and NEXT loops back to itself. */
vrh.vring.desc[1].addr = (unsigned long)ind;
vrh.vring.desc[1].len = sizeof(*ind);
vrh.vring.desc[1].flags = VRING_DESC_F_INDIRECT | VRING_DESC_F_NEXT;
vrh.vring.desc[1].next = 1;
/* Publish head 1 on the avail ring. */
vrh.vring.avail->ring[0] = 1;
vrh.vring.avail->idx = 1;
vringh_iov_init(&riov, host_riov, ARRAY_SIZE(host_riov));
vringh_iov_init(&wiov, host_wiov, ARRAY_SIZE(host_wiov));
err = vringh_getdesc_user(&vrh, &riov, &wiov, getrange, &head);
if (err != -ELOOP)
errx(1, "self-referential indirect: %i not -ELOOP", err);
vringh_iov_cleanup(&riov);
vringh_iov_cleanup(&wiov);
/* Don't leak memory... */
vring_del_virtqueue(vq);
free(__user_addr_min);