Add set_rid_user and unset_rid_user support
Subsystems (3)
cover Add set_rid_user and unset_rid_user support
This is a pair of new uAPI/ops for user space to set a user space virtual Request ID for a passthrough PCI device, down to the IOMMU driver. This's primarily used by SMMUv3 driver for now, to link the vSID and the pSID of a device behind the SMMU. The link (lookup table) will be used to verify an ATC_INV command from the user space for that device, and then replace the virtual SID with the corresponding physical SID.
This is a RFC series, since I am not confident at all about the uAPI/ops namings. Hopefully we can finalize this small series soon and include it in the next version of SMMUv3 nesting series.
This series is available on Github:
https://github.com/nicolinc/iommufd/commits/set_rid_user-rfc-v1
Thanks!
Nicolin
Nicolin Chen (3):
iommu: Add set/unset_rid_user ops
iommufd: Add IOMMUFD_CMD_DEVICE_SET_RID and
IOMMUFD_CMD_DEVICE_UNSET_RID
iommu/arm-smmu-v3: Implement set_rid_user and unset_rid_user
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 45 +++++++++++++++++++++ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 2 + drivers/iommu/iommufd/device.c | 39 ++++++++++++++++++ drivers/iommu/iommufd/iommufd_private.h | 2 + drivers/iommu/iommufd/main.c | 4 ++ include/linux/iommu.h | 4 ++ include/uapi/linux/iommufd.h | 31 ++++++++++++++ 7 files changed, 127 insertions(+)
Signature · 1 line
2.40.0
RFC PATCH 1/3 iommu: Add set/unset_rid_user ops
Add a pair of new ops to allow user space to set virtual RID of a device. This can be converted to Stream ID for SMMUv3, so as to create the link between a physical Stream ID and a virtual Stream ID.
--- include/linux/iommu.h | 4 ++++ 1 file changed, 4 insertions(+)
include/linux/iommu.h +4 -0
@@ -248,6 +248,7 @@ struct iommu_iotlb_gather {* driver init to device driver init (default no) * @dev_enable/disable_feat: per device entries to enable/disable * iommu specific features. +* @set/unset_rid_user: per device entries to set/unset user space Request ID * @page_response: handle page request response * @def_domain_type: device default domain type, return value: * - IOMMU_DOMAIN_IDENTITY: must use an identity domain
@@ -291,6 +292,9 @@ struct iommu_ops {int (*dev_enable_feat)(struct device *dev,enum iommu_dev_features f);int (*dev_disable_feat)(struct device *dev,enum iommu_dev_features f); +int (*set_rid_user)(struct device *dev,u32 rid,u32 rid_base); +int (*unset_rid_user)(struct device *dev); +int (*page_response)(struct device *dev,struct iommu_fault_event *evt,struct iommu_page_response *msg);
Signature · 1 line
2.40.0
RFC PATCH 2/3 iommufd: Add IOMMUFD_CMD_DEVICE_SET_RID and IOMMUFD_CMD_DEVICE_UNSET_RID
In reply to Nicolin ChenAdd a new pair of ioctls to allow user space to set user space Request ID for a passthrough PCI device. This is to create a link between a physical RID and a virtual RID in the IOMMU driver that cares.
--- drivers/iommu/iommufd/device.c | 39 +++++++++++++++++++++++++ drivers/iommu/iommufd/iommufd_private.h | 2 ++ drivers/iommu/iommufd/main.c | 4 +++ include/uapi/linux/iommufd.h | 31 ++++++++++++++++++++ 4 files changed, 76 insertions(+)
drivers/iommu/iommufd/device.c +39 -0
@@ -735,6 +735,45 @@ void iommufd_device_detach(struct iommufd_device *idev) }EXPORT_SYMBOL_NS_GPL (iommufd_device_detach,IOMMUFD ); +int iommufd_device_set_rid (struct iommufd_ucmd *ucmd) +{ +struct iommufd_device_set_rid *cmd = ucmd->cmd; +struct iommufd_device *idev; +const struct iommu_ops *ops; +int rc =0 ; + +if (!cmd->rid) +return -EINVAL ; + + idev =iommufd_get_device (ucmd, cmd->dev_id); +if (IS_ERR (idev)) +return PTR_ERR (idev); + + ops =dev_iommu_ops (idev->dev); +if (ops && ops->set_rid_user) + rc = ops->set_rid_user (idev->dev, cmd->rid, cmd->rid_base); +iommufd_put_object (&idev->obj); +return rc; +} + +int iommufd_device_unset_rid (struct iommufd_ucmd *ucmd) +{ +struct iommufd_device_unset_rid *cmd = ucmd->cmd; +struct iommufd_device *idev; +const struct iommu_ops *ops; +int rc =0 ; + + idev =iommufd_get_device (ucmd, cmd->dev_id); +if (IS_ERR (idev)) +return PTR_ERR (idev); + + ops =dev_iommu_ops (idev->dev); +if (ops && ops->unset_rid_user) + rc = ops->unset_rid_user (idev->dev); +iommufd_put_object (&idev->obj); +return rc; +} +void iommufd_access_destroy_object (struct iommufd_object *obj) { struct iommufd_access *access =
drivers/iommu/iommufd/iommufd_private.h +2 -0
@@ -320,6 +320,8 @@ 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); +int iommufd_device_set_rid (struct iommufd_ucmd *ucmd); +int iommufd_device_unset_rid (struct iommufd_ucmd *ucmd);extern const u64 iommufd_hwpt_type_bitmaps[];
drivers/iommu/iommufd/main.c +4 -0
@@ -317,6 +317,10 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = { val64),IOCTL_OP (IOMMU_VFIO_IOAS ,iommufd_vfio_ioas ,struct iommu_vfio_ioas ,__reserved ), +IOCTL_OP (IOMMU_DEVICE_SET_RID ,iommufd_device_set_rid , +struct iommufd_device_set_rid ,rid_base ), +IOCTL_OP (IOMMU_DEVICE_UNSET_RID ,iommufd_device_unset_rid , +struct iommufd_device_unset_rid ,dev_id ), #ifdef CONFIG_IOMMUFD_TEST IOCTL_OP(IOMMU_TEST_CMD, iommufd_test, struct iommu_test_cmd, last), #endif
include/uapi/linux/iommufd.h +31 -0
@@ -48,6 +48,8 @@ enum {IOMMUFD_CMD_HWPT_ALLOC ,IOMMUFD_CMD_DEVICE_GET_HW_INFO ,IOMMUFD_CMD_HWPT_INVALIDATE , +IOMMUFD_CMD_DEVICE_SET_RID , + IOMMUFD_CMD_DEVICE_UNSET_RID, };/**
@@ -482,4 +484,33 @@ struct iommu_hwpt_invalidate { __aligned_u64 data_uptr; };#define IOMMU_HWPT_INVALIDATE _IO(IOMMUFD_TYPE, IOMMUFD_CMD_HWPT_INVALIDATE) + +/** +* struct iommufd_device_set_rid - ioctl(IOMMU_DEVICE_SET_RID) +* @size: sizeof(struct iommufd_device_set_rid) +* @dev_id: The device to set a Request ID +* @rid: PCI Request ID +* Bits [15:8] are the Bus number. +* Bits [7:3] are the Device number. +* Bits [2:0] are the Function number. +* @rid_base: Base ID to map PCI Request ID +*/ +struct iommufd_device_set_rid { +__u32 size; +__u32 dev_id; +__u32 rid; +__u32 rid_base; +}; +#define IOMMU_DEVICE_SET_RID _IO(IOMMUFD_TYPE, IOMMUFD_CMD_DEVICE_SET_RID) + +/** +* struct iommufd_device_unset_rid - ioctl(IOMMU_DEVICE_UNSET_RID) +* @size: sizeof(struct iommufd_device_unset_rid) +* @dev_id: The device to unset a Request ID +*/ +struct iommufd_device_unset_rid { +__u32 size; +__u32 dev_id; +}; +#define IOMMU_DEVICE_UNSET_RID _IO(IOMMUFD_TYPE, IOMMUFD_CMD_DEVICE_UNSET_RID)#endif
Signature · 1 line
2.40.0
RFC PATCH 3/3 iommu/arm-smmu-v3: Implement set_rid_user and unset_rid_user
In reply to Nicolin ChenImplement the new set_rid_user and unset_rid_user ops, by using an xarray to store the stream pointer indexed by a given user space Stream ID. This will be used by user cache invalidation hypercall, to check the SID field of an ATC_INV command and replace it with the corresponding physical SID.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 45 +++++++++++++++++++++ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 2 + 2 files changed, 47 insertions(+)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +45 -0
@@ -2843,6 +2843,48 @@ static void arm_smmu_remove_dev_pasid(struct device *dev, ioasid_t pasid)arm_smmu_sva_remove_dev_pasid (domain, dev, pasid); } +static int arm_smmu_set_rid_user (struct device *dev,u32 rid,u32 rid_base) +{ +struct arm_smmu_master *master =dev_iommu_priv_get (dev); +struct arm_smmu_stream *stream = &master->streams[0 ]; +struct arm_smmu_device *smmu = master->smmu; +u32 sid_user = rid_base | rid; +int ret =0 ; + +if (!sid_user) +return -EINVAL ; + + ret =xa_alloc (&smmu->streams_user, &sid_user, stream, +XA_LIMIT (sid_user, sid_user),GFP_KERNEL_ACCOUNT ); +if (ret) +return ret; + stream->id_user = sid_user; +return 0 ; +} + +static int arm_smmu_unset_rid_user (struct device *dev) +{ +struct arm_smmu_master *master =dev_iommu_priv_get (dev); +struct arm_smmu_stream *stream = &master->streams[0 ]; +struct arm_smmu_device *smmu = master->smmu; +u32 sid_user = stream->id_user; + +if (!sid_user) +return -ENODEV ; + +xa_lock (&smmu->streams_user); + stream =__xa_erase (&smmu->streams_user, sid_user); +if (stream != master->streams) { +WARN_ON (__xa_alloc (&smmu->streams_user, &sid_user, stream, +XA_LIMIT (sid_user, sid_user), +GFP_KERNEL_ACCOUNT )); +xa_unlock (&smmu->streams_user); +return -EINVAL ; + } +xa_unlock (&smmu->streams_user); +return 0 ; +} +static struct iommu_ops arm_smmu_ops = { .capable = arm_smmu_capable, .domain_alloc = arm_smmu_domain_alloc,
@@ -2854,6 +2896,8 @@ static struct iommu_ops arm_smmu_ops = { .remove_dev_pasid = arm_smmu_remove_dev_pasid, .dev_enable_feat = arm_smmu_dev_enable_feature, .dev_disable_feat = arm_smmu_dev_disable_feature, + .set_rid_user = arm_smmu_set_rid_user, + .unset_rid_user = arm_smmu_unset_rid_user, .page_response = arm_smmu_page_response, .def_domain_type = arm_smmu_def_domain_type, .pgsize_bitmap = -1UL, /* Restricted during device attach */
@@ -3082,6 +3126,7 @@ static int arm_smmu_init_structures(struct arm_smmu_device *smmu)mutex_init (&smmu->streams_mutex); smmu->streams =RB_ROOT ; +xa_init_flags (&smmu->streams_user,XA_FLAGS_ALLOC1 |XA_FLAGS_ACCOUNT ); ret =arm_smmu_init_queues (smmu);if (ret)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +2 -0
@@ -674,10 +674,12 @@ struct arm_smmu_device {struct rb_root streams;struct mutex streams_mutex; +struct xarray streams_user; };struct arm_smmu_stream {u32 id; +u32 id_user;struct arm_smmu_master *master;struct rb_node node; };
Signature · 1 line
2.40.0