Message ID | 20230511143024.19542-4-yi.l.liu@intel.com |
---|---|
State | New |
Headers | show |
Series | iommufd: Add iommu hardware info reporting | expand |
On 5/15/23 2:14 PM, Liu, Yi L wrote: >> -----Original Message----- >> From: Baolu Lu<baolu.lu@linux.intel.com> >> Sent: Friday, May 12, 2023 1:39 PM >> To: Liu, Yi L<yi.l.liu@intel.com>;joro@8bytes.org;alex.williamson@redhat.com; >> jgg@nvidia.com; Tian, Kevin<kevin.tian@intel.com>;robin.murphy@arm.com >> Cc:baolu.lu@linux.intel.com;cohuck@redhat.com;eric.auger@redhat.com; >> nicolinc@nvidia.com;kvm@vger.kernel.org;mjrosato@linux.ibm.com; >> chao.p.peng@linux.intel.com;yi.y.sun@linux.intel.com;peterx@redhat.com; >> jasowang@redhat.com;shameerali.kolothum.thodi@huawei.com;lulu@redhat.com; >> suravee.suthikulpanit@amd.com;iommu@lists.linux.dev;linux-kernel@vger.kernel.org; >> linux-kselftest@vger.kernel.org; Duan, Zhenzhong<zhenzhong.duan@intel.com> >> Subject: Re: [PATCH v3 3/4] iommufd: Add IOMMU_DEVICE_GET_HW_INFO >> >> On 5/11/23 10:30 PM, Yi Liu wrote: >>> Under nested IOMMU translation, userspace owns the stage-1 translation >>> table (e.g. the stage-1 page table of Intel VT-d or the context table >>> of ARM SMMUv3, and etc.). Stage-1 translation tables are vendor specific, >>> and needs to be compatiable with the underlying IOMMU hardware. Hence, >>> userspace should know the IOMMU hardware capability before creating and >>> configuring the stage-1 translation table to kernel. >>> >>> This adds IOMMU_DEVICE_GET_HW_INFO to query the IOMMU hardware >> information >>> for a given device. The returned data is vendor specific, userspace needs >>> to decode it with the structure mapped by the @out_data_type field. >>> >>> As only physical devices have IOMMU hardware, so this will return error >>> if the given device is not a physical device. >>> >>> Co-developed-by: Nicolin Chen<nicolinc@nvidia.com> >>> Signed-off-by: Nicolin Chen<nicolinc@nvidia.com> >>> Signed-off-by: Yi Liu<yi.l.liu@intel.com> >>> --- >>> drivers/iommu/iommufd/device.c | 72 +++++++++++++++++++++++++ >>> drivers/iommu/iommufd/iommufd_private.h | 1 + >>> drivers/iommu/iommufd/main.c | 3 ++ >>> include/uapi/linux/iommufd.h | 37 +++++++++++++ >>> 4 files changed, 113 insertions(+) >>> >>> diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c >>> index 051bd8e99858..bc99d092de8f 100644 >>> --- a/drivers/iommu/iommufd/device.c >>> +++ b/drivers/iommu/iommufd/device.c >>> @@ -263,6 +263,78 @@ u32 iommufd_device_to_id(struct iommufd_device *idev) >>> } >>> EXPORT_SYMBOL_NS_GPL(iommufd_device_to_id, IOMMUFD); >>> >>> +static int iommufd_zero_fill_user(u64 ptr, int bytes) >>> +{ >>> + int index = 0; >>> + >>> + for (; index < bytes; index++) { >>> + if (put_user(0, (uint8_t __user *)u64_to_user_ptr(ptr + index))) >>> + return -EFAULT; >>> + } >>> + return 0; >>> +} >>> + >>> +int iommufd_device_get_hw_info(struct iommufd_ucmd *ucmd) >>> +{ >>> + struct iommu_hw_info *cmd = ucmd->cmd; >>> + unsigned int length = 0, data_len; >>> + struct iommufd_device *idev; >>> + const struct iommu_ops *ops; >>> + void *data = NULL; >>> + int rc = 0; >>> + >>> + if (cmd->flags || cmd->__reserved || !cmd->data_len) >>> + return -EOPNOTSUPP; >>> + >>> + idev = iommufd_get_device(ucmd, cmd->dev_id); >>> + if (IS_ERR(idev)) >>> + return PTR_ERR(idev); >>> + >>> + ops = dev_iommu_ops(idev->dev); >>> + if (!ops->hw_info) >>> + goto done; >> If the iommu driver doesn't provide a hw_info callback, it still >> returns success? > Yes, as noted in the cover letter. It's for a remark from Jason. In such > case, the out_data_type is NULL, it means no specific data is filled > in the buffer pointed by cmd->data_ptr. > > - Let IOMMU_DEVICE_GET_HW_INFO succeed even the underlying iommu driver > does not have driver-specific data to report per below remark. > https://lore.kernel.org/kvm/ZAcwJSK%2F9UVI9LXu@nvidia.com/ Oh, I overlooked that. Thanks for the explanation. It's fair enough. Best regards, baolu
> From: Tian, Kevin > Sent: Friday, May 19, 2023 4:42 PM > > +struct iommu_hw_info { > > + __u32 size; > > + __u32 flags; > > + __u32 dev_id; > > + __u32 data_len; > > + __aligned_u64 data_ptr; > > + __u32 out_data_type; > > + __u32 __reserved; > > it's unusual to have reserved field in the end. It makes more sense > to move data_ptr to the end to make it meaningful. > Please ignore this comment. typed too fast...
Hi Kevin, On Fri, May 19, 2023 at 08:42:07AM +0000, Tian, Kevin wrote: > > +}; > > +#define IOMMU_DEVICE_GET_HW_INFO _IO(IOMMUFD_TYPE, > > IOMMUFD_CMD_DEVICE_GET_HW_INFO) > > #endif > > Here we have a naming confusion. > > 'IOMMU' is the prefix of iommufd ioctls. > > 'DEVICE' is the subjective. > > Then "GET_HW_INFO" implies getting hardware info related to > this device. then it should not be restricted to the iommu info. > > with that it's clearer to call it IOMMU_DEVICE_GET_IOMMU_INFO. Though the entire ioctl is tied to the input "dev_id", I think it isn't really about the device corresponding to the dev_id, similar to the IOMMU_HWPT_ALLOC having a dev_id input too. So, I think the "IOMMU_DEVICE" here should be interpreted simply as "an iommu device". We could also highlight this somewhere in the header. With that being said, IOMMU_DEVICE_SET/UNSET_DATA should be renamed to IOMMU_DEVICE_SET/UNSET_DEV_DATA -- "DEVICE" is the iommu device while the "DEV_DATA" is a given device that's behind the iommu. > similarly for struct iommu_hw_info. > > 'iommu' is the prefix for all iommufd ioctl structures. > > then 'hw_info' is too broard. > > iommu_device_iommu_info reads better? though having two > iommu's in the name is a little bit annoying... How about: IOMMU_DEVICE_GET_FEATURES struct iommu_device_features ? Thanks Nic
diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 051bd8e99858..bc99d092de8f 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -263,6 +263,78 @@ u32 iommufd_device_to_id(struct iommufd_device *idev) } EXPORT_SYMBOL_NS_GPL(iommufd_device_to_id, IOMMUFD); +static int iommufd_zero_fill_user(u64 ptr, int bytes) +{ + int index = 0; + + for (; index < bytes; index++) { + if (put_user(0, (uint8_t __user *)u64_to_user_ptr(ptr + index))) + return -EFAULT; + } + return 0; +} + +int iommufd_device_get_hw_info(struct iommufd_ucmd *ucmd) +{ + struct iommu_hw_info *cmd = ucmd->cmd; + unsigned int length = 0, data_len; + struct iommufd_device *idev; + const struct iommu_ops *ops; + void *data = NULL; + int rc = 0; + + if (cmd->flags || cmd->__reserved || !cmd->data_len) + return -EOPNOTSUPP; + + idev = iommufd_get_device(ucmd, cmd->dev_id); + if (IS_ERR(idev)) + return PTR_ERR(idev); + + ops = dev_iommu_ops(idev->dev); + if (!ops->hw_info) + goto done; + + /* driver has hw_info callback should have a unique hw_info_type */ + if (ops->hw_info_type == IOMMU_HW_INFO_TYPE_NONE) { + pr_warn_ratelimited("iommu driver set an invalid type\n"); + rc = -ENODEV; + goto out_err; + } + + data = ops->hw_info(idev->dev, &data_len); + if (IS_ERR(data)) { + rc = PTR_ERR(data); + goto out_err; + } + + length = min(cmd->data_len, data_len); + if (copy_to_user(u64_to_user_ptr(cmd->data_ptr), data, length)) { + rc = -EFAULT; + goto out_err; + } + + /* + * Zero the trailing bytes if the user buffer is bigger than the + * data size kernel actually has. + */ + if (length < cmd->data_len) { + rc = iommufd_zero_fill_user(cmd->data_ptr + length, + cmd->data_len - length); + if (rc) + goto out_err; + } + +done: + cmd->data_len = length; + cmd->out_data_type = ops->hw_info_type; + rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd)); + +out_err: + kfree(data); + iommufd_put_object(&idev->obj); + return rc; +} + static int iommufd_group_setup_msi(struct iommufd_group *igroup, struct iommufd_hw_pagetable *hwpt) { diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h index dba730129b8c..69d6bb61d387 100644 --- a/drivers/iommu/iommufd/iommufd_private.h +++ b/drivers/iommu/iommufd/iommufd_private.h @@ -308,6 +308,7 @@ iommufd_get_device(struct iommufd_ucmd *ucmd, u32 id) } void iommufd_device_destroy(struct iommufd_object *obj); +int iommufd_device_get_hw_info(struct iommufd_ucmd *ucmd); struct iommufd_access { struct iommufd_object obj; diff --git a/drivers/iommu/iommufd/main.c b/drivers/iommu/iommufd/main.c index 3932fe26522b..5c24e8971f09 100644 --- a/drivers/iommu/iommufd/main.c +++ b/drivers/iommu/iommufd/main.c @@ -269,6 +269,7 @@ static int iommufd_option(struct iommufd_ucmd *ucmd) union ucmd_buffer { struct iommu_destroy destroy; struct iommu_hwpt_alloc hwpt; + struct iommu_hw_info info; struct iommu_ioas_alloc alloc; struct iommu_ioas_allow_iovas allow_iovas; struct iommu_ioas_copy ioas_copy; @@ -302,6 +303,8 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = { IOCTL_OP(IOMMU_DESTROY, iommufd_destroy, struct iommu_destroy, id), IOCTL_OP(IOMMU_HWPT_ALLOC, iommufd_hwpt_alloc, struct iommu_hwpt_alloc, __reserved), + IOCTL_OP(IOMMU_DEVICE_GET_HW_INFO, iommufd_device_get_hw_info, + struct iommu_hw_info, __reserved), IOCTL_OP(IOMMU_IOAS_ALLOC, iommufd_ioas_alloc_ioctl, struct iommu_ioas_alloc, out_ioas_id), IOCTL_OP(IOMMU_IOAS_ALLOW_IOVAS, iommufd_ioas_allow_iovas, diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h index 99bf0715f545..e9d42838dcbd 100644 --- a/include/uapi/linux/iommufd.h +++ b/include/uapi/linux/iommufd.h @@ -46,6 +46,7 @@ enum { IOMMUFD_CMD_OPTION, IOMMUFD_CMD_VFIO_IOAS, IOMMUFD_CMD_HWPT_ALLOC, + IOMMUFD_CMD_DEVICE_GET_HW_INFO, }; /** @@ -377,4 +378,40 @@ struct iommu_hwpt_alloc { enum iommu_hw_info_type { IOMMU_HW_INFO_TYPE_NONE, }; + +/** + * struct iommu_hw_info - ioctl(IOMMU_DEVICE_GET_HW_INFO) + * @size: sizeof(struct iommu_hw_info) + * @flags: Must be 0 + * @dev_id: The device bound to the iommufd + * @data_len: Input the length of the user buffer in bytes. Output the + * length of data filled in the user buffer. + * @data_ptr: Pointer to the user buffer + * @out_data_type: Output the iommu hardware info type as defined by + * enum iommu_hw_info_type. + * @__reserved: Must be 0 + * + * Query the hardware iommu information for given device which has been + * bound to iommufd. @data_len is the size of the buffer which captures + * iommu type specific data and the data will be filled. Trailing bytes + * are zeroed if the user buffer is larger than the data kernel has. + * + * The type specific data would be used to sync capability between the + * virtual IOMMU and the hardware IOMMU. e.g. nested translation requires + * to check the hardware IOMMU capability so guest stage-1 page table + * uses a format compatible to the hardware IOMMU. + * + * The @out_data_type will be filled if the ioctl succeeds. It would + * be used to decode the data filled in the buffer pointed by @data_ptr. + */ +struct iommu_hw_info { + __u32 size; + __u32 flags; + __u32 dev_id; + __u32 data_len; + __aligned_u64 data_ptr; + __u32 out_data_type; + __u32 __reserved; +}; +#define IOMMU_DEVICE_GET_HW_INFO _IO(IOMMUFD_TYPE, IOMMUFD_CMD_DEVICE_GET_HW_INFO) #endif