mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
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:
commit
69e2b55fc3
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user