Skip to content

Commit

Permalink
iommu/iommufd: Report iova range to userspace
Browse files Browse the repository at this point in the history
[HACK. will fix in v2]

IOVA range is critical info for userspace to manage DMA for an I/O address
space. This patch reports the valid iova range info of a given device.

Due to aforementioned hack, this info comes from the hacked vfio type1
driver. To follow the same format in vfio, we also introduce a cap chain
format in IOMMU_DEVICE_GET_INFO to carry the iova range info.

Signed-off-by: Liu Yi L <yi.l.liu@intel.com>
  • Loading branch information
yiliu1765 authored and intel-lab-lkp committed Sep 19, 2021
1 parent 990e3f1 commit 89ec01e
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 5 deletions.
2 changes: 2 additions & 0 deletions drivers/iommu/iommu.c
Expand Up @@ -2752,6 +2752,7 @@ void iommu_get_resv_regions(struct device *dev, struct list_head *list)
if (ops && ops->get_resv_regions)
ops->get_resv_regions(dev, list);
}
EXPORT_SYMBOL_GPL(iommu_get_resv_regions);

void iommu_put_resv_regions(struct device *dev, struct list_head *list)
{
Expand All @@ -2760,6 +2761,7 @@ void iommu_put_resv_regions(struct device *dev, struct list_head *list)
if (ops && ops->put_resv_regions)
ops->put_resv_regions(dev, list);
}
EXPORT_SYMBOL_GPL(iommu_put_resv_regions);

/**
* generic_iommu_put_resv_regions - Reserved region driver helper
Expand Down
41 changes: 40 additions & 1 deletion drivers/iommu/iommufd/iommufd.c
Expand Up @@ -19,6 +19,7 @@
#include <linux/iommufd.h>
#include <linux/xarray.h>
#include <asm-generic/bug.h>
#include <linux/vfio.h>

/* Per iommufd */
struct iommufd_ctx {
Expand Down Expand Up @@ -298,6 +299,38 @@ iommu_find_device_from_cookie(struct iommufd_ctx *ictx, u64 dev_cookie)
return dev;
}

static int iommu_device_add_cap_chain(struct device *dev, unsigned long arg,
struct iommu_device_info *info)
{
struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
int ret;

ret = vfio_device_add_iova_cap(dev, &caps);
if (ret)
return ret;

if (caps.size) {
info->flags |= IOMMU_DEVICE_INFO_CAPS;

if (info->argsz < sizeof(*info) + caps.size) {
info->argsz = sizeof(*info) + caps.size;
} else {
vfio_info_cap_shift(&caps, sizeof(*info));
if (copy_to_user((void __user *)arg +
sizeof(*info), caps.buf,
caps.size)) {
kfree(caps.buf);
info->flags &= ~IOMMU_DEVICE_INFO_CAPS;
return -EFAULT;
}
info->cap_offset = sizeof(*info);
}

kfree(caps.buf);
}
return 0;
}

static void iommu_device_build_info(struct device *dev,
struct iommu_device_info *info)
{
Expand All @@ -324,8 +357,9 @@ static int iommufd_get_device_info(struct iommufd_ctx *ictx,
struct iommu_device_info info;
unsigned long minsz;
struct device *dev;
int ret;

minsz = offsetofend(struct iommu_device_info, addr_width);
minsz = offsetofend(struct iommu_device_info, cap_offset);

if (copy_from_user(&info, (void __user *)arg, minsz))
return -EFAULT;
Expand All @@ -341,6 +375,11 @@ static int iommufd_get_device_info(struct iommufd_ctx *ictx,

iommu_device_build_info(dev, &info);

info.cap_offset = 0;
ret = iommu_device_add_cap_chain(dev, arg, &info);
if (ret)
pr_info_ratelimited("No cap chain added, error %d\n", ret);

return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
}

Expand Down
47 changes: 43 additions & 4 deletions drivers/vfio/vfio_iommu_type1.c
Expand Up @@ -2963,15 +2963,15 @@ static int vfio_iommu_iova_add_cap(struct vfio_info_cap *caps,
return 0;
}

static int vfio_iommu_iova_build_caps(struct vfio_iommu *iommu,
struct vfio_info_cap *caps)
static int vfio_iova_list_build_caps(struct list_head *iova_list,
struct vfio_info_cap *caps)
{
struct vfio_iommu_type1_info_cap_iova_range *cap_iovas;
struct vfio_iova *iova;
size_t size;
int iovas = 0, i = 0, ret;

list_for_each_entry(iova, &iommu->iova_list, list)
list_for_each_entry(iova, iova_list, list)
iovas++;

if (!iovas) {
Expand All @@ -2990,7 +2990,7 @@ static int vfio_iommu_iova_build_caps(struct vfio_iommu *iommu,

cap_iovas->nr_iovas = iovas;

list_for_each_entry(iova, &iommu->iova_list, list) {
list_for_each_entry(iova, iova_list, list) {
cap_iovas->iova_ranges[i].start = iova->start;
cap_iovas->iova_ranges[i].end = iova->end;
i++;
Expand All @@ -3002,6 +3002,45 @@ static int vfio_iommu_iova_build_caps(struct vfio_iommu *iommu,
return ret;
}

static int vfio_iommu_iova_build_caps(struct vfio_iommu *iommu,
struct vfio_info_cap *caps)
{
return vfio_iova_list_build_caps(&iommu->iova_list, caps);
}

/* HACK: called by /dev/iommu core to build iova range cap for a device */
int vfio_device_add_iova_cap(struct device *dev, struct vfio_info_cap *caps)
{
u64 awidth;
dma_addr_t aperture_end;
LIST_HEAD(iova);
LIST_HEAD(dev_resv_regions);
int ret;

ret = iommu_device_get_info(dev, IOMMU_DEV_INFO_ADDR_WIDTH, &awidth);
if (ret)
return ret;

/* FIXME: needs to use geometry info reported by iommu core. */
aperture_end = ((dma_addr_t)1) << awidth;

ret = vfio_iommu_iova_insert(&iova, 0, aperture_end);
if (ret)
return ret;

iommu_get_resv_regions(dev, &dev_resv_regions);
ret = vfio_iommu_resv_exclude(&iova, &dev_resv_regions);
if (ret)
goto out;

ret = vfio_iova_list_build_caps(&iova, caps);
out:
vfio_iommu_iova_free(&iova);
iommu_put_resv_regions(dev, &dev_resv_regions);
return ret;
}
EXPORT_SYMBOL_GPL(vfio_device_add_iova_cap);

static int vfio_iommu_migration_build_caps(struct vfio_iommu *iommu,
struct vfio_info_cap *caps)
{
Expand Down
2 changes: 2 additions & 0 deletions include/linux/vfio.h
Expand Up @@ -212,6 +212,8 @@ extern int vfio_info_add_capability(struct vfio_info_cap *caps,
extern int vfio_set_irqs_validate_and_prepare(struct vfio_irq_set *hdr,
int num_irqs, int max_irq_type,
size_t *data_size);
extern int vfio_device_add_iova_cap(struct device *dev,
struct vfio_info_cap *caps);

struct pci_dev;
#if IS_ENABLED(CONFIG_VFIO_SPAPR_EEH)
Expand Down
3 changes: 3 additions & 0 deletions include/uapi/linux/iommu.h
Expand Up @@ -68,6 +68,7 @@
* +---------------+------------+
* ...
* @addr_width: the address width of supported I/O address spaces.
* @cap_offset: Offset within info struct of first cap
*
* Availability: after device is bound to iommufd
*/
Expand All @@ -77,9 +78,11 @@ struct iommu_device_info {
#define IOMMU_DEVICE_INFO_ENFORCE_SNOOP (1 << 0) /* IOMMU enforced snoop */
#define IOMMU_DEVICE_INFO_PGSIZES (1 << 1) /* supported page sizes */
#define IOMMU_DEVICE_INFO_ADDR_WIDTH (1 << 2) /* addr_wdith field valid */
#define IOMMU_DEVICE_INFO_CAPS (1 << 3) /* info supports cap chain */
__u64 dev_cookie;
__u64 pgsize_bitmap;
__u32 addr_width;
__u32 cap_offset;
};

#define IOMMU_DEVICE_GET_INFO _IO(IOMMU_TYPE, IOMMU_BASE + 1)
Expand Down

0 comments on commit 89ec01e

Please sign in to comment.