On Sun, Sep 19, 2021 at 02:38:38PM +0800, Liu Yi L wrote: > After a device is bound to the iommufd, userspace can use this interface > to query the underlying iommu capability and format info for this device. > Based on this information the user then creates I/O address space in a > compatible format with the to-be-attached devices. > > Device cookie which is registered at binding time is used to mark the > device which is being queried here. > > Signed-off-by: Liu Yi L > --- > drivers/iommu/iommufd/iommufd.c | 68 +++++++++++++++++++++++++++++++++ > include/uapi/linux/iommu.h | 49 ++++++++++++++++++++++++ > 2 files changed, 117 insertions(+) > > diff --git a/drivers/iommu/iommufd/iommufd.c b/drivers/iommu/iommufd/iommufd.c > index e16ca21e4534..641f199f2d41 100644 > --- a/drivers/iommu/iommufd/iommufd.c > +++ b/drivers/iommu/iommufd/iommufd.c > @@ -117,6 +117,71 @@ static int iommufd_fops_release(struct inode *inode, struct file *filep) > return 0; > } > > +static struct device * > +iommu_find_device_from_cookie(struct iommufd_ctx *ictx, u64 dev_cookie) > +{ > + struct iommufd_device *idev; > + struct device *dev = NULL; > + unsigned long index; > + > + mutex_lock(&ictx->lock); > + xa_for_each(&ictx->device_xa, index, idev) { > + if (idev->dev_cookie == dev_cookie) { > + dev = idev->dev; > + break; > + } > + } > + mutex_unlock(&ictx->lock); > + > + return dev; > +} > + > +static void iommu_device_build_info(struct device *dev, > + struct iommu_device_info *info) > +{ > + bool snoop; > + u64 awidth, pgsizes; > + > + if (!iommu_device_get_info(dev, IOMMU_DEV_INFO_FORCE_SNOOP, &snoop)) > + info->flags |= snoop ? IOMMU_DEVICE_INFO_ENFORCE_SNOOP : 0; > + > + if (!iommu_device_get_info(dev, IOMMU_DEV_INFO_PAGE_SIZE, &pgsizes)) { > + info->pgsize_bitmap = pgsizes; > + info->flags |= IOMMU_DEVICE_INFO_PGSIZES; > + } > + > + if (!iommu_device_get_info(dev, IOMMU_DEV_INFO_ADDR_WIDTH, &awidth)) { > + info->addr_width = awidth; > + info->flags |= IOMMU_DEVICE_INFO_ADDR_WIDTH; > + } > +} > + > +static int iommufd_get_device_info(struct iommufd_ctx *ictx, > + unsigned long arg) > +{ > + struct iommu_device_info info; > + unsigned long minsz; > + struct device *dev; > + > + minsz = offsetofend(struct iommu_device_info, addr_width); > + > + if (copy_from_user(&info, (void __user *)arg, minsz)) > + return -EFAULT; > + > + if (info.argsz < minsz) > + return -EINVAL; > + > + info.flags = 0; > + > + dev = iommu_find_device_from_cookie(ictx, info.dev_cookie); > + if (!dev) > + return -EINVAL; > + > + iommu_device_build_info(dev, &info); > + > + return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0; > +} > + > static long iommufd_fops_unl_ioctl(struct file *filep, > unsigned int cmd, unsigned long arg) > { > @@ -127,6 +192,9 @@ static long iommufd_fops_unl_ioctl(struct file *filep, > return ret; > > switch (cmd) { > + case IOMMU_DEVICE_GET_INFO: > + ret = iommufd_get_device_info(ictx, arg); > + break; > default: > pr_err_ratelimited("unsupported cmd %u\n", cmd); > break; > diff --git a/include/uapi/linux/iommu.h b/include/uapi/linux/iommu.h > index 59178fc229ca..76b71f9d6b34 100644 > --- a/include/uapi/linux/iommu.h > +++ b/include/uapi/linux/iommu.h > @@ -7,6 +7,55 @@ > #define _UAPI_IOMMU_H > > #include > +#include > + > +/* -------- IOCTLs for IOMMU file descriptor (/dev/iommu) -------- */ > + > +#define IOMMU_TYPE (';') > +#define IOMMU_BASE 100 > + > +/* > + * IOMMU_DEVICE_GET_INFO - _IOR(IOMMU_TYPE, IOMMU_BASE + 1, > + * struct iommu_device_info) > + * > + * Check IOMMU capabilities and format information on a bound device. > + * > + * The device is identified by device cookie (registered when binding > + * this device). > + * > + * @argsz: user filled size of this data. > + * @flags: tells userspace which capability info is available > + * @dev_cookie: user assinged cookie. > + * @pgsize_bitmap: Bitmap of supported page sizes. 1-setting of the > + * bit in pgsize_bitmap[63:12] indicates a supported > + * page size. Details as below table: > + * > + * +===============+============+ > + * | Bit[index] | Page Size | > + * +---------------+------------+ > + * | 12 | 4 KB | > + * +---------------+------------+ > + * | 13 | 8 KB | > + * +---------------+------------+ > + * | 14 | 16 KB | > + * +---------------+------------+ > + * ... > + * @addr_width: the address width of supported I/O address spaces. > + * > + * Availability: after device is bound to iommufd > + */ > +struct iommu_device_info { > + __u32 argsz; > + __u32 flags; > +#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 */ > + __u64 dev_cookie; > + __u64 pgsize_bitmap; > + __u32 addr_width; I think this is where you should be reporting available IOVA windows, rather than just an address width. I know that for ppc a real situation will be to have two different windows of different sizes: that is the effective address width depends on which IOVA window you're mapping into. > +}; > + > +#define IOMMU_DEVICE_GET_INFO _IO(IOMMU_TYPE, IOMMU_BASE + 1) > > #define IOMMU_FAULT_PERM_READ (1 << 0) /* read */ > #define IOMMU_FAULT_PERM_WRITE (1 << 1) /* write */ -- David Gibson | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson