Skip to content

Commit

Permalink
vdpa: Support transferring virtual addressing during DMA mapping
Browse files Browse the repository at this point in the history
This patch introduces an attribute for vDPA device to indicate
whether virtual address can be used. If vDPA device driver set
it, vhost-vdpa bus driver will not pin user page and transfer
userspace virtual address instead of physical address during
DMA mapping. And corresponding vma->vm_file and offset will be
also passed as an opaque pointer.

Suggested-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: Xie Yongji <xieyongji@bytedance.com>
Acked-by: Jason Wang <jasowang@redhat.com>
Link: https://lore.kernel.org/r/20210831103634.33-11-xieyongji@bytedance.com
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
  • Loading branch information
Xie Yongji authored and Michael S. Tsirkin committed Sep 6, 2021
1 parent 22af48c commit d8945ec
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 19 deletions.
2 changes: 1 addition & 1 deletion drivers/vdpa/ifcvf/ifcvf_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ static int ifcvf_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name)
pdev = ifcvf_mgmt_dev->pdev;
dev = &pdev->dev;
adapter = vdpa_alloc_device(struct ifcvf_adapter, vdpa,
dev, &ifc_vdpa_ops, name);
dev, &ifc_vdpa_ops, name, false);
if (IS_ERR(adapter)) {
IFCVF_ERR(pdev, "Failed to allocate vDPA structure");
return PTR_ERR(adapter);
Expand Down
2 changes: 1 addition & 1 deletion drivers/vdpa/mlx5/net/mlx5_vnet.c
Original file line number Diff line number Diff line change
Expand Up @@ -2425,7 +2425,7 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name)
max_vqs = min_t(u32, max_vqs, MLX5_MAX_SUPPORTED_VQS);

ndev = vdpa_alloc_device(struct mlx5_vdpa_net, mvdev.vdev, mdev->device, &mlx5_vdpa_ops,
name);
name, false);
if (IS_ERR(ndev))
return PTR_ERR(ndev);

Expand Down
9 changes: 8 additions & 1 deletion drivers/vdpa/vdpa.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ static void vdpa_release_dev(struct device *d)
* @config: the bus operations that is supported by this device
* @size: size of the parent structure that contains private data
* @name: name of the vdpa device; optional.
* @use_va: indicate whether virtual address must be used by this device
*
* Driver should use vdpa_alloc_device() wrapper macro instead of
* using this directly.
Expand All @@ -80,7 +81,8 @@ static void vdpa_release_dev(struct device *d)
*/
struct vdpa_device *__vdpa_alloc_device(struct device *parent,
const struct vdpa_config_ops *config,
size_t size, const char *name)
size_t size, const char *name,
bool use_va)
{
struct vdpa_device *vdev;
int err = -EINVAL;
Expand All @@ -91,6 +93,10 @@ struct vdpa_device *__vdpa_alloc_device(struct device *parent,
if (!!config->dma_map != !!config->dma_unmap)
goto err;

/* It should only work for the device that use on-chip IOMMU */
if (use_va && !(config->dma_map || config->set_map))
goto err;

err = -ENOMEM;
vdev = kzalloc(size, GFP_KERNEL);
if (!vdev)
Expand All @@ -106,6 +112,7 @@ struct vdpa_device *__vdpa_alloc_device(struct device *parent,
vdev->index = err;
vdev->config = config;
vdev->features_valid = false;
vdev->use_va = use_va;

if (name)
err = dev_set_name(&vdev->dev, "%s", name);
Expand Down
2 changes: 1 addition & 1 deletion drivers/vdpa/vdpa_sim/vdpa_sim.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ struct vdpasim *vdpasim_create(struct vdpasim_dev_attr *dev_attr)
ops = &vdpasim_config_ops;

vdpasim = vdpa_alloc_device(struct vdpasim, vdpa, NULL, ops,
dev_attr->name);
dev_attr->name, false);
if (IS_ERR(vdpasim)) {
ret = PTR_ERR(vdpasim);
goto err_alloc;
Expand Down
2 changes: 1 addition & 1 deletion drivers/vdpa/virtio_pci/vp_vdpa.c
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ static int vp_vdpa_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return ret;

vp_vdpa = vdpa_alloc_device(struct vp_vdpa, vdpa,
dev, &vp_vdpa_ops, NULL);
dev, &vp_vdpa_ops, NULL, false);
if (IS_ERR(vp_vdpa)) {
dev_err(dev, "vp_vdpa: Failed to allocate vDPA structure\n");
return PTR_ERR(vp_vdpa);
Expand Down
99 changes: 88 additions & 11 deletions drivers/vhost/vdpa.c
Original file line number Diff line number Diff line change
Expand Up @@ -526,8 +526,28 @@ static void vhost_vdpa_pa_unmap(struct vhost_vdpa *v, u64 start, u64 last)
}
}

static void vhost_vdpa_va_unmap(struct vhost_vdpa *v, u64 start, u64 last)
{
struct vhost_dev *dev = &v->vdev;
struct vhost_iotlb *iotlb = dev->iotlb;
struct vhost_iotlb_map *map;
struct vdpa_map_file *map_file;

while ((map = vhost_iotlb_itree_first(iotlb, start, last)) != NULL) {
map_file = (struct vdpa_map_file *)map->opaque;
fput(map_file->file);
kfree(map_file);
vhost_iotlb_map_free(iotlb, map);
}
}

static void vhost_vdpa_iotlb_unmap(struct vhost_vdpa *v, u64 start, u64 last)
{
struct vdpa_device *vdpa = v->vdpa;

if (vdpa->use_va)
return vhost_vdpa_va_unmap(v, start, last);

return vhost_vdpa_pa_unmap(v, start, last);
}

Expand Down Expand Up @@ -562,35 +582,37 @@ static int perm_to_iommu_flags(u32 perm)
return flags | IOMMU_CACHE;
}

static int vhost_vdpa_map(struct vhost_vdpa *v,
u64 iova, u64 size, u64 pa, u32 perm)
static int vhost_vdpa_map(struct vhost_vdpa *v, u64 iova,
u64 size, u64 pa, u32 perm, void *opaque)
{
struct vhost_dev *dev = &v->vdev;
struct vdpa_device *vdpa = v->vdpa;
const struct vdpa_config_ops *ops = vdpa->config;
int r = 0;

r = vhost_iotlb_add_range(dev->iotlb, iova, iova + size - 1,
pa, perm);
r = vhost_iotlb_add_range_ctx(dev->iotlb, iova, iova + size - 1,
pa, perm, opaque);
if (r)
return r;

if (ops->dma_map) {
r = ops->dma_map(vdpa, iova, size, pa, perm, NULL);
r = ops->dma_map(vdpa, iova, size, pa, perm, opaque);
} else if (ops->set_map) {
if (!v->in_batch)
r = ops->set_map(vdpa, dev->iotlb);
} else {
r = iommu_map(v->domain, iova, pa, size,
perm_to_iommu_flags(perm));
}

if (r)
if (r) {
vhost_iotlb_del_range(dev->iotlb, iova, iova + size - 1);
else
return r;
}

if (!vdpa->use_va)
atomic64_add(PFN_DOWN(size), &dev->mm->pinned_vm);

return r;
return 0;
}

static void vhost_vdpa_unmap(struct vhost_vdpa *v, u64 iova, u64 size)
Expand All @@ -611,6 +633,56 @@ static void vhost_vdpa_unmap(struct vhost_vdpa *v, u64 iova, u64 size)
}
}

static int vhost_vdpa_va_map(struct vhost_vdpa *v,
u64 iova, u64 size, u64 uaddr, u32 perm)
{
struct vhost_dev *dev = &v->vdev;
u64 offset, map_size, map_iova = iova;
struct vdpa_map_file *map_file;
struct vm_area_struct *vma;
int ret;

mmap_read_lock(dev->mm);

while (size) {
vma = find_vma(dev->mm, uaddr);
if (!vma) {
ret = -EINVAL;
break;
}
map_size = min(size, vma->vm_end - uaddr);
if (!(vma->vm_file && (vma->vm_flags & VM_SHARED) &&
!(vma->vm_flags & (VM_IO | VM_PFNMAP))))
goto next;

map_file = kzalloc(sizeof(*map_file), GFP_KERNEL);
if (!map_file) {
ret = -ENOMEM;
break;
}
offset = (vma->vm_pgoff << PAGE_SHIFT) + uaddr - vma->vm_start;
map_file->offset = offset;
map_file->file = get_file(vma->vm_file);
ret = vhost_vdpa_map(v, map_iova, map_size, uaddr,
perm, map_file);
if (ret) {
fput(map_file->file);
kfree(map_file);
break;
}
next:
size -= map_size;
uaddr += map_size;
map_iova += map_size;
}
if (ret)
vhost_vdpa_unmap(v, iova, map_iova - iova);

mmap_read_unlock(dev->mm);

return ret;
}

static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
u64 iova, u64 size, u64 uaddr, u32 perm)
{
Expand Down Expand Up @@ -677,7 +749,7 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
csize = PFN_PHYS(last_pfn - map_pfn + 1);
ret = vhost_vdpa_map(v, iova, csize,
PFN_PHYS(map_pfn),
perm);
perm, NULL);
if (ret) {
/*
* Unpin the pages that are left unmapped
Expand Down Expand Up @@ -706,7 +778,7 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,

/* Pin the rest chunk */
ret = vhost_vdpa_map(v, iova, PFN_PHYS(last_pfn - map_pfn + 1),
PFN_PHYS(map_pfn), perm);
PFN_PHYS(map_pfn), perm, NULL);
out:
if (ret) {
if (nchunks) {
Expand Down Expand Up @@ -739,6 +811,7 @@ static int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v,
struct vhost_iotlb_msg *msg)
{
struct vhost_dev *dev = &v->vdev;
struct vdpa_device *vdpa = v->vdpa;
struct vhost_iotlb *iotlb = dev->iotlb;

if (msg->iova < v->range.first || !msg->size ||
Expand All @@ -750,6 +823,10 @@ static int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v,
msg->iova + msg->size - 1))
return -EEXIST;

if (vdpa->use_va)
return vhost_vdpa_va_map(v, msg->iova, msg->size,
msg->uaddr, msg->perm);

return vhost_vdpa_pa_map(v, msg->iova, msg->size, msg->uaddr,
msg->perm);
}
Expand Down
20 changes: 17 additions & 3 deletions include/linux/vdpa.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ struct vdpa_mgmt_dev;
* @config: the configuration ops for this device.
* @index: device index
* @features_valid: were features initialized? for legacy guests
* @use_va: indicate whether virtual address must be used by this device
* @nvqs: maximum number of supported virtqueues
* @mdev: management device pointer; caller must setup when registering device as part
* of dev_add() mgmtdev ops callback before invoking _vdpa_register_device().
Expand All @@ -75,6 +76,7 @@ struct vdpa_device {
const struct vdpa_config_ops *config;
unsigned int index;
bool features_valid;
bool use_va;
int nvqs;
struct vdpa_mgmt_dev *mdev;
};
Expand All @@ -89,6 +91,16 @@ struct vdpa_iova_range {
u64 last;
};

/**
* Corresponding file area for device memory mapping
* @file: vma->vm_file for the mapping
* @offset: mapping offset in the vm_file
*/
struct vdpa_map_file {
struct file *file;
u64 offset;
};

/**
* struct vdpa_config_ops - operations for configuring a vDPA device.
* Note: vDPA device drivers are required to implement all of the
Expand Down Expand Up @@ -279,7 +291,8 @@ struct vdpa_config_ops {

struct vdpa_device *__vdpa_alloc_device(struct device *parent,
const struct vdpa_config_ops *config,
size_t size, const char *name);
size_t size, const char *name,
bool use_va);

/**
* vdpa_alloc_device - allocate and initilaize a vDPA device
Expand All @@ -289,15 +302,16 @@ struct vdpa_device *__vdpa_alloc_device(struct device *parent,
* @parent: the parent device
* @config: the bus operations that is supported by this device
* @name: name of the vdpa device
* @use_va: indicate whether virtual address must be used by this device
*
* Return allocated data structure or ERR_PTR upon error
*/
#define vdpa_alloc_device(dev_struct, member, parent, config, name) \
#define vdpa_alloc_device(dev_struct, member, parent, config, name, use_va) \
container_of(__vdpa_alloc_device( \
parent, config, \
sizeof(dev_struct) + \
BUILD_BUG_ON_ZERO(offsetof( \
dev_struct, member)), name), \
dev_struct, member)), name, use_va), \
dev_struct, member)

int vdpa_register_device(struct vdpa_device *vdev, int nvqs);
Expand Down

0 comments on commit d8945ec

Please sign in to comment.