vdpa: Support transferring virtual addressing during DMA mapping
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>
This commit is contained in:
parent
22af48cf91
commit
d8945ec411
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
@ -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;
|
||||
@ -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)
|
||||
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
@ -562,21 +582,21 @@ 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);
|
||||
@ -584,13 +604,15 @@ static int vhost_vdpa_map(struct vhost_vdpa *v,
|
||||
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)
|
||||
@ -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)
|
||||
{
|
||||
@ -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
|
||||
@ -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) {
|
||||
@ -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 ||
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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().
|
||||
@ -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;
|
||||
};
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user