diff --git a/MAINTAINERS b/MAINTAINERS index fde7147bfa1e..fe23913b1e43 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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) diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index c8bfbf8435ac..ddbb05d3e98c 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -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); diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index f9982217247d..45d87c0614a5 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -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); } diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c index 587fc13197f1..fa72ae23b8af 100644 --- a/drivers/iommu/virtio-iommu.c +++ b/drivers/iommu/virtio-iommu.c @@ -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); diff --git a/drivers/vdpa/mlx5/net/mlx5_vnet.c b/drivers/vdpa/mlx5/net/mlx5_vnet.c index 8563fec2855d..0fcb18b5f30a 100644 --- a/drivers/vdpa/mlx5/net/mlx5_vnet.c +++ b/drivers/vdpa/mlx5/net/mlx5_vnet.c @@ -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) { diff --git a/drivers/vdpa/solidrun/snet_ctrl.c b/drivers/vdpa/solidrun/snet_ctrl.c index e284c3a06717..09afa0487fd6 100644 --- a/drivers/vdpa/solidrun/snet_ctrl.c +++ b/drivers/vdpa/solidrun/snet_ctrl.c @@ -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); diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c index 47c6c3d23f5c..6455f837dd26 100644 --- a/drivers/vdpa/vdpa.c +++ b/drivers/vdpa/vdpa.c @@ -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; } diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c index cfa88a60a38f..97d437d89524 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c @@ -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; } diff --git a/drivers/vdpa/vdpa_user/iova_domain.c b/drivers/vdpa/vdpa_user/iova_domain.c index b6c958224b7c..28a977248a0d 100644 --- a/drivers/vdpa/vdpa_user/iova_domain.c +++ b/drivers/vdpa/vdpa_user/iova_domain.c @@ -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; diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index 49a231bdf948..f953353b50b4 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -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; } diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 885fac5ed042..e44044449cef 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -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; } diff --git a/drivers/vhost/test.c b/drivers/vhost/test.c index 84ed9ea81219..65dc8c71336d 100644 --- a/drivers/vhost/test.c +++ b/drivers/vhost/test.c @@ -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); diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index ff7d90719171..c912ac40ca37 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -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; diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 4d5d32e68d35..ea127af58ada 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -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; diff --git a/drivers/vhost/vringh.c b/drivers/vhost/vringh.c index 74836eb60c7a..5094715dd8bc 100644 --- a/drivers/vhost/vringh.c +++ b/drivers/vhost/vringh.c @@ -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; diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index ab3e3d887e00..8c1b47ec17a2 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -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 diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index faae58e3401a..e2ec0ec01d67 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -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); diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index b90c174450b2..79d3395f3705 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -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; } diff --git a/drivers/virtio/virtio_pci_common.h b/drivers/virtio/virtio_pci_common.h index 8cd01de27baf..4ce903e7d3a9 100644 --- a/drivers/virtio/virtio_pci_common.h +++ b/drivers/virtio/virtio_pci_common.h @@ -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); diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index db678f5a80e0..f683a2d6a200 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -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; diff --git a/include/uapi/linux/virtio_spi.h b/include/uapi/linux/virtio_spi.h index 8ab3c970cdd3..459c2276c938 100644 --- a/include/uapi/linux/virtio_spi.h +++ b/include/uapi/linux/virtio_spi.h @@ -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. */ diff --git a/tools/virtio/vringh_test.c b/tools/virtio/vringh_test.c index 84961b9ab5ff..2a5d7f7d2d6c 100644 --- a/tools/virtio/vringh_test.c +++ b/tools/virtio/vringh_test.c @@ -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);