Message ID | 20230511143024.19542-4-yi.l.liu@intel.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | iommufd: Add iommu hardware info reporting | expand |
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? > + > + /* 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) > { Best regards, baolu
> -----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/ Regards, Yi Liu > > + > > + /* 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) > > { > > Best regards, > baolu
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: Liu, Yi L <yi.l.liu@intel.com> > Sent: Thursday, May 11, 2023 10:30 PM > + > +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; > + } this should be a WARN_ON_ONCE() since it's a driver bug. > + > + 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)); the 'done' label should be moved before above zero_fill. Otherwise in !ops->hw_info case the user buffer is not cleared. > union ucmd_buffer { > struct iommu_destroy destroy; > struct iommu_hwpt_alloc hwpt; > + struct iommu_hw_info info; follow alphabetic order this should be ahead of hwpt. > @@ -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), before IOMMU_HWPT_ALLOC > + > +/** > + * 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; 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. > +}; > +#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. 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...
> 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
> From: Nicolin Chen <nicolinc@nvidia.com> > Sent: Saturday, May 20, 2023 2:30 AM > > 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. yes this is a good view of it. with that it's not necessary to have a 'DEVICE' notation in the name which looks confusing with dev_id. Just IOMMU_GET_HW_INFO for the iommu behind the specified dev_id. then keep the structure name as iommu_hw_info. > > 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. this then becomes IOMMU_SET/UNSET_DEV_DATA. > > > 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
On Wed, May 24, 2023 at 05:00:40AM +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. > > yes this is a good view of it. with that it's not necessary to have > a 'DEVICE' notation in the name which looks confusing with dev_id. > > Just IOMMU_GET_HW_INFO for the iommu behind the specified dev_id. > > then keep the structure name as iommu_hw_info. That'd be neat. > > 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. > > this then becomes IOMMU_SET/UNSET_DEV_DATA. Ack. 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