* [PATCH v7 01/13] iommu: Add iommu_attach_device_pasid_handle()
2025-02-16 3:52 [PATCH v7 00/13] iommufd support pasid attach/replace Yi Liu
@ 2025-02-16 3:52 ` Yi Liu
2025-02-25 9:47 ` Tian, Kevin
2025-02-16 3:52 ` [PATCH v7 02/13] iommu: Introduce a replace API for device pasid Yi Liu
` (11 subsequent siblings)
12 siblings, 1 reply; 17+ messages in thread
From: Yi Liu @ 2025-02-16 3:52 UTC (permalink / raw)
To: joro, kevin.tian, baolu.lu, jgg
Cc: yi.l.liu, iommu, robin.murphy, nicolinc, will, vasant.hegde
The existing iommu_attach_device_pasid() function allows both a valid
handle and a NULL handle, which is not consistent with the RID path where
iommu_attach_group() and iommu_attach_group_handle() coexist. To refine
it, this adds iommu_attach_device_pasid_handle() to cover the case with
valid handle, while let the iommu_attach_device_pasid() only deals with
the case with NULL handle.
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
---
drivers/dma/idxd/init.c | 2 +-
drivers/iommu/iommu-sva.c | 10 ++++++----
drivers/iommu/iommu.c | 8 ++++----
include/linux/iommu.h | 34 +++++++++++++++++++++++++++++-----
4 files changed, 40 insertions(+), 14 deletions(-)
diff --git a/drivers/dma/idxd/init.c b/drivers/dma/idxd/init.c
index b946f78f85e1..d11a763ef124 100644
--- a/drivers/dma/idxd/init.c
+++ b/drivers/dma/idxd/init.c
@@ -593,7 +593,7 @@ static int idxd_enable_system_pasid(struct idxd_device *idxd)
* DMA domain is owned by the driver, it should support all valid
* types such as DMA-FQ, identity, etc.
*/
- ret = iommu_attach_device_pasid(domain, dev, pasid, NULL);
+ ret = iommu_attach_device_pasid(domain, dev, pasid);
if (ret) {
dev_err(dev, "failed to attach device pasid %d, domain type %d",
pasid, domain->type);
diff --git a/drivers/iommu/iommu-sva.c b/drivers/iommu/iommu-sva.c
index 503c5d23c1ea..09d676ddf15e 100644
--- a/drivers/iommu/iommu-sva.c
+++ b/drivers/iommu/iommu-sva.c
@@ -115,8 +115,9 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm
/* Search for an existing domain. */
list_for_each_entry(domain, &mm->iommu_mm->sva_domains, next) {
- ret = iommu_attach_device_pasid(domain, dev, iommu_mm->pasid,
- &handle->handle);
+ ret = iommu_attach_device_pasid_handle(domain, dev,
+ iommu_mm->pasid,
+ &handle->handle);
if (!ret) {
domain->users++;
goto out;
@@ -130,8 +131,9 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm
goto out_free_handle;
}
- ret = iommu_attach_device_pasid(domain, dev, iommu_mm->pasid,
- &handle->handle);
+ ret = iommu_attach_device_pasid_handle(domain, dev,
+ iommu_mm->pasid,
+ &handle->handle);
if (ret)
goto out_free_domain;
domain->users = 1;
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index c3bf8e131a8e..95830f670e18 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -3423,9 +3423,9 @@ static void *iommu_make_pasid_entry(struct iommu_domain *domain,
*
* Return: 0 on success, or an error.
*/
-int iommu_attach_device_pasid(struct iommu_domain *domain,
- struct device *dev, ioasid_t pasid,
- struct iommu_attach_handle *handle)
+int __iommu_attach_device_pasid(struct iommu_domain *domain,
+ struct device *dev, ioasid_t pasid,
+ struct iommu_attach_handle *handle)
{
/* Caller must be a probed driver on dev */
struct iommu_group *group = dev->iommu_group;
@@ -3476,7 +3476,7 @@ int iommu_attach_device_pasid(struct iommu_domain *domain,
mutex_unlock(&group->mutex);
return ret;
}
-EXPORT_SYMBOL_GPL(iommu_attach_device_pasid);
+EXPORT_SYMBOL_GPL(__iommu_attach_device_pasid);
/*
* iommu_detach_device_pasid() - Detach the domain from pasid of device
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 38c65e92ecd0..d795de7bad8f 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -1122,9 +1122,24 @@ bool iommu_group_dma_owner_claimed(struct iommu_group *group);
int iommu_device_claim_dma_owner(struct device *dev, void *owner);
void iommu_device_release_dma_owner(struct device *dev);
-int iommu_attach_device_pasid(struct iommu_domain *domain,
- struct device *dev, ioasid_t pasid,
- struct iommu_attach_handle *handle);
+int __iommu_attach_device_pasid(struct iommu_domain *domain,
+ struct device *dev, ioasid_t pasid,
+ struct iommu_attach_handle *handle);
+
+static inline int iommu_attach_device_pasid(struct iommu_domain *domain,
+ struct device *dev, ioasid_t pasid)
+{
+ return __iommu_attach_device_pasid(domain, dev, pasid, NULL);
+}
+
+static inline int
+iommu_attach_device_pasid_handle(struct iommu_domain *domain,
+ struct device *dev, ioasid_t pasid,
+ struct iommu_attach_handle *handle)
+{
+ return __iommu_attach_device_pasid(domain, dev, pasid, handle);
+}
+
void iommu_detach_device_pasid(struct iommu_domain *domain,
struct device *dev, ioasid_t pasid);
ioasid_t iommu_alloc_global_pasid(struct device *dev);
@@ -1139,6 +1154,8 @@ struct iommu_fault_param {};
struct iommu_iotlb_gather {};
struct iommu_dirty_bitmap {};
struct iommu_dirty_ops {};
+struct iommu_attach_handle {};
+
static inline bool device_iommu_capable(struct device *dev, enum iommu_cap cap)
{
@@ -1451,8 +1468,15 @@ static inline int iommu_device_claim_dma_owner(struct device *dev, void *owner)
}
static inline int iommu_attach_device_pasid(struct iommu_domain *domain,
- struct device *dev, ioasid_t pasid,
- struct iommu_attach_handle *handle)
+ struct device *dev, ioasid_t pasid)
+{
+ return -ENODEV;
+}
+
+static inline int
+iommu_attach_device_pasid_handle(struct iommu_domain *domain,
+ struct device *dev, ioasid_t pasid,
+ struct iommu_attach_handle *handle)
{
return -ENODEV;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* RE: [PATCH v7 01/13] iommu: Add iommu_attach_device_pasid_handle()
2025-02-16 3:52 ` [PATCH v7 01/13] iommu: Add iommu_attach_device_pasid_handle() Yi Liu
@ 2025-02-25 9:47 ` Tian, Kevin
0 siblings, 0 replies; 17+ messages in thread
From: Tian, Kevin @ 2025-02-25 9:47 UTC (permalink / raw)
To: Liu, Yi L, joro@8bytes.org, baolu.lu@linux.intel.com,
jgg@nvidia.com
Cc: iommu@lists.linux.dev, robin.murphy@arm.com, nicolinc@nvidia.com,
will@kernel.org, vasant.hegde@amd.com
> From: Liu, Yi L <yi.l.liu@intel.com>
> Sent: Sunday, February 16, 2025 11:52 AM
>
> The existing iommu_attach_device_pasid() function allows both a valid
> handle and a NULL handle, which is not consistent with the RID path where
> iommu_attach_group() and iommu_attach_group_handle() coexist. To refine
> it, this adds iommu_attach_device_pasid_handle() to cover the case with
> valid handle, while let the iommu_attach_device_pasid() only deals with
> the case with NULL handle.
>
> Signed-off-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v7 02/13] iommu: Introduce a replace API for device pasid
2025-02-16 3:52 [PATCH v7 00/13] iommufd support pasid attach/replace Yi Liu
2025-02-16 3:52 ` [PATCH v7 01/13] iommu: Add iommu_attach_device_pasid_handle() Yi Liu
@ 2025-02-16 3:52 ` Yi Liu
2025-02-25 9:55 ` Tian, Kevin
2025-02-16 3:52 ` [PATCH v7 03/13] iommufd: Pass @pasid through the device attach/replace path Yi Liu
` (10 subsequent siblings)
12 siblings, 1 reply; 17+ messages in thread
From: Yi Liu @ 2025-02-16 3:52 UTC (permalink / raw)
To: joro, kevin.tian, baolu.lu, jgg
Cc: yi.l.liu, iommu, robin.murphy, nicolinc, will, vasant.hegde
Provide a high-level API to allow replacements of one domain with
another for specific pasid of a device. This is similar to
iommu_group_replace_domain_handle() and it is expected to be used
only by IOMMUFD.
Co-developed-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
---
drivers/iommu/iommu-priv.h | 4 ++
drivers/iommu/iommu.c | 105 +++++++++++++++++++++++++++++++++----
2 files changed, 100 insertions(+), 9 deletions(-)
diff --git a/drivers/iommu/iommu-priv.h b/drivers/iommu/iommu-priv.h
index fedc57754a48..9b2072fadfdd 100644
--- a/drivers/iommu/iommu-priv.h
+++ b/drivers/iommu/iommu-priv.h
@@ -27,6 +27,10 @@ static inline const struct iommu_ops *iommu_fwspec_ops(struct iommu_fwspec *fwsp
int iommu_group_replace_domain(struct iommu_group *group,
struct iommu_domain *new_domain);
+int iommu_replace_device_pasid_handle(struct iommu_domain *domain,
+ struct device *dev, ioasid_t pasid,
+ struct iommu_attach_handle *handle);
+
int iommu_device_register_bus(struct iommu_device *iommu,
const struct iommu_ops *ops,
const struct bus_type *bus,
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 95830f670e18..3a68ebe3ab25 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -522,14 +522,10 @@ static void iommu_deinit_device(struct device *dev)
DEFINE_MUTEX(iommu_probe_device_lock);
-struct iommu_domain *iommu_group_domain(struct iommu_group *group)
+static struct iommu_domain *pasid_entry_to_domain(void *pasid_entry)
{
struct iommu_domain *domain;
- void *pasid_entry;
-
- lockdep_assert_held(&group->mutex);
- pasid_entry = xa_load(&group->pasid_array, IOMMU_NO_PASID);
if (xa_pointer_tag(pasid_entry) == IOMMU_PASID_ARRAY_HANDLE) {
struct iommu_attach_handle *handle;
@@ -542,6 +538,13 @@ struct iommu_domain *iommu_group_domain(struct iommu_group *group)
return domain;
}
+struct iommu_domain *iommu_group_domain(struct iommu_group *group)
+{
+ lockdep_assert_held(&group->mutex);
+
+ return pasid_entry_to_domain(xa_load(&group->pasid_array, IOMMU_NO_PASID));
+}
+
static int __iommu_probe_device(struct device *dev, struct list_head *group_list)
{
struct iommu_domain *gdomain;
@@ -3369,14 +3372,15 @@ static void iommu_remove_dev_pasid(struct device *dev, ioasid_t pasid,
}
static int __iommu_set_group_pasid(struct iommu_domain *domain,
- struct iommu_group *group, ioasid_t pasid)
+ struct iommu_group *group, ioasid_t pasid,
+ struct iommu_domain *old)
{
struct group_device *device, *last_gdev;
int ret;
for_each_group_device(group, device) {
ret = domain->ops->set_dev_pasid(domain, device->dev,
- pasid, NULL);
+ pasid, old);
if (ret)
goto err_revert;
}
@@ -3388,7 +3392,20 @@ static int __iommu_set_group_pasid(struct iommu_domain *domain,
for_each_group_device(group, device) {
if (device == last_gdev)
break;
- iommu_remove_dev_pasid(device->dev, pasid, domain);
+ /* If no old domain, undo the succeeded devices/pasid */
+ if (!old) {
+ iommu_remove_dev_pasid(device->dev, pasid, domain);
+ continue;
+ }
+
+ /*
+ * Rollback the succeeded devices/pasid to the old domain.
+ * And it is a driver bug to fail attaching with a previously
+ * good domain.
+ */
+ if (WARN_ON(old->ops->set_dev_pasid(old, device->dev,
+ pasid, domain)))
+ iommu_remove_dev_pasid(device->dev, pasid, domain);
}
return ret;
}
@@ -3462,7 +3479,7 @@ int __iommu_attach_device_pasid(struct iommu_domain *domain,
goto out_unlock;
}
- ret = __iommu_set_group_pasid(domain, group, pasid);
+ ret = __iommu_set_group_pasid(domain, group, pasid, NULL);
if (ret)
goto out_unlock;
@@ -3478,6 +3495,76 @@ int __iommu_attach_device_pasid(struct iommu_domain *domain,
}
EXPORT_SYMBOL_GPL(__iommu_attach_device_pasid);
+/**
+ * iommu_replace_device_pasid_handle - Replace the domain that a pasid
+ * is attached to
+ * @domain: the new iommu domain
+ * @dev: the attached device.
+ * @pasid: the pasid of the device.
+ * @handle: the attach handle.
+ *
+ * This API allows the pasid to switch domains. The @pasid should have been
+ * attached via iommu_replace_device_pasid_handle(), otherwise, this fails.
+ * The pasid will keep the old configuration if replacement failed.
+ * Return 0 on success, or an error.
+ */
+int iommu_replace_device_pasid_handle(struct iommu_domain *domain,
+ struct device *dev, ioasid_t pasid,
+ struct iommu_attach_handle *handle)
+{
+ /* Caller must be a probed driver on dev */
+ struct iommu_group *group = dev->iommu_group;
+ struct iommu_attach_handle *pasid_entry;
+ struct iommu_domain *curr_domain;
+ void *curr;
+ int ret;
+
+ if (!group)
+ return -ENODEV;
+
+ if (!domain->ops->set_dev_pasid)
+ return -EOPNOTSUPP;
+
+ if (dev_iommu_ops(dev) != domain->owner ||
+ pasid == IOMMU_NO_PASID || !handle)
+ return -EINVAL;
+
+ handle->domain = domain;
+
+ mutex_lock(&group->mutex);
+ curr = xa_load(&group->pasid_array, pasid);
+ /* Not a replace case */
+ if (!curr) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ curr_domain = pasid_entry_to_domain(curr);
+
+ if (curr_domain == domain) {
+ ret = 0;
+ goto out_unlock;
+ }
+
+ ret = __iommu_set_group_pasid(domain, group, pasid, curr_domain);
+ if (ret)
+ goto out_unlock;
+
+ pasid_entry = iommu_make_pasid_entry(domain, handle);
+
+ curr = xa_store(&group->pasid_array, pasid, pasid_entry, GFP_KERNEL);
+ if (xa_is_err(curr)) {
+ ret = xa_err(curr);
+ WARN_ON(__iommu_set_group_pasid(curr_domain, group,
+ pasid, domain));
+ }
+
+out_unlock:
+ mutex_unlock(&group->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(iommu_replace_device_pasid_handle, "IOMMUFD_INTERNAL");
+
/*
* iommu_detach_device_pasid() - Detach the domain from pasid of device
* @domain: the iommu domain.
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* RE: [PATCH v7 02/13] iommu: Introduce a replace API for device pasid
2025-02-16 3:52 ` [PATCH v7 02/13] iommu: Introduce a replace API for device pasid Yi Liu
@ 2025-02-25 9:55 ` Tian, Kevin
2025-02-25 11:35 ` Yi Liu
0 siblings, 1 reply; 17+ messages in thread
From: Tian, Kevin @ 2025-02-25 9:55 UTC (permalink / raw)
To: Liu, Yi L, joro@8bytes.org, baolu.lu@linux.intel.com,
jgg@nvidia.com
Cc: iommu@lists.linux.dev, robin.murphy@arm.com, nicolinc@nvidia.com,
will@kernel.org, vasant.hegde@amd.com
> From: Liu, Yi L <yi.l.liu@intel.com>
> Sent: Sunday, February 16, 2025 11:52 AM
>
> -struct iommu_domain *iommu_group_domain(struct iommu_group *group)
> +static struct iommu_domain *pasid_entry_to_domain(void *pasid_entry)
> {
> struct iommu_domain *domain;
> - void *pasid_entry;
> -
> - lockdep_assert_held(&group->mutex);
>
> - pasid_entry = xa_load(&group->pasid_array, IOMMU_NO_PASID);
> if (xa_pointer_tag(pasid_entry) == IOMMU_PASID_ARRAY_HANDLE) {
> struct iommu_attach_handle *handle;
>
What about calling it iommu_group_pasid_domain(group, pasid)
which accepts a pasid as parameter so the caller doesn't need to
do its own xa_load?
> @@ -3388,7 +3392,20 @@ static int __iommu_set_group_pasid(struct
> iommu_domain *domain,
> for_each_group_device(group, device) {
> if (device == last_gdev)
> break;
> - iommu_remove_dev_pasid(device->dev, pasid, domain);
> + /* If no old domain, undo the succeeded devices/pasid */
> + if (!old) {
> + iommu_remove_dev_pasid(device->dev, pasid,
> domain);
> + continue;
> + }
> +
> + /*
> + * Rollback the succeeded devices/pasid to the old domain.
> + * And it is a driver bug to fail attaching with a previously
> + * good domain.
> + */
> + if (WARN_ON(old->ops->set_dev_pasid(old, device->dev,
> + pasid, domain)))
> + iommu_remove_dev_pasid(device->dev, pasid,
> domain);
Above can be simplified as:
if (!old || WARN_ON(old...))
iommu_remove_dev_pasid();
> +/**
> + * iommu_replace_device_pasid_handle - Replace the domain that a pasid
> + * is attached to
> + * @domain: the new iommu domain
> + * @dev: the attached device.
> + * @pasid: the pasid of the device.
> + * @handle: the attach handle.
> + *
> + * This API allows the pasid to switch domains. The @pasid should have
> been
> + * attached via iommu_replace_device_pasid_handle(), otherwise, this
why do we care how the pasid has been attached? and in reality people
does attach, replace, replace, etc, i.e. you cannot expect the 1st operation
as a replace.
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v7 02/13] iommu: Introduce a replace API for device pasid
2025-02-25 9:55 ` Tian, Kevin
@ 2025-02-25 11:35 ` Yi Liu
0 siblings, 0 replies; 17+ messages in thread
From: Yi Liu @ 2025-02-25 11:35 UTC (permalink / raw)
To: Tian, Kevin, joro@8bytes.org, baolu.lu@linux.intel.com,
jgg@nvidia.com
Cc: iommu@lists.linux.dev, robin.murphy@arm.com, nicolinc@nvidia.com,
will@kernel.org, vasant.hegde@amd.com
On 2025/2/25 17:55, Tian, Kevin wrote:
>> From: Liu, Yi L <yi.l.liu@intel.com>
>> Sent: Sunday, February 16, 2025 11:52 AM
>>
>> -struct iommu_domain *iommu_group_domain(struct iommu_group *group)
>> +static struct iommu_domain *pasid_entry_to_domain(void *pasid_entry)
>> {
>> struct iommu_domain *domain;
>> - void *pasid_entry;
>> -
>> - lockdep_assert_held(&group->mutex);
>>
>> - pasid_entry = xa_load(&group->pasid_array, IOMMU_NO_PASID);
>> if (xa_pointer_tag(pasid_entry) == IOMMU_PASID_ARRAY_HANDLE) {
>> struct iommu_attach_handle *handle;
>>
>
> What about calling it iommu_group_pasid_domain(group, pasid)
> which accepts a pasid as parameter so the caller doesn't need to
> do its own xa_load?
I would consider it in the new version. However, if the caller still
has the entry, I may still need this helper to avoid duplicated
xa_load().
>> @@ -3388,7 +3392,20 @@ static int __iommu_set_group_pasid(struct
>> iommu_domain *domain,
>> for_each_group_device(group, device) {
>> if (device == last_gdev)
>> break;
>> - iommu_remove_dev_pasid(device->dev, pasid, domain);
>> + /* If no old domain, undo the succeeded devices/pasid */
>> + if (!old) {
>> + iommu_remove_dev_pasid(device->dev, pasid,
>> domain);
>> + continue;
>> + }
>> +
>> + /*
>> + * Rollback the succeeded devices/pasid to the old domain.
>> + * And it is a driver bug to fail attaching with a previously
>> + * good domain.
>> + */
>> + if (WARN_ON(old->ops->set_dev_pasid(old, device->dev,
>> + pasid, domain)))
>> + iommu_remove_dev_pasid(device->dev, pasid,
>> domain);
>
> Above can be simplified as:
>
> if (!old || WARN_ON(old...))
> iommu_remove_dev_pasid();
yes.
>
>> +/**
>> + * iommu_replace_device_pasid_handle - Replace the domain that a pasid
>> + * is attached to
>> + * @domain: the new iommu domain
>> + * @dev: the attached device.
>> + * @pasid: the pasid of the device.
>> + * @handle: the attach handle.
>> + *
>> + * This API allows the pasid to switch domains. The @pasid should have
>> been
>> + * attached via iommu_replace_device_pasid_handle(), otherwise, this
>
> why do we care how the pasid has been attached? and in reality people
> does attach, replace, replace, etc, i.e. you cannot expect the 1st operation
> as a replace.
it's a typo. :) It should have been iommu_attach_device_pasid_handle().
However, this comment should be dropped anyhow since we are going to
support replacing domain with handle per the handle series. I'll have
it in a refreshed version of this series.
The replace path is going to have something like the below. If both domain
and handle is the same, it shall return directly.
pasid_entry = iommu_make_pasid_entry(new_domain, handle);
curr = xa_cmpxchg(&group->pasid_array, IOMMU_NO_PASID, NULL,
XA_ZERO_ENTRY, GFP_KERNEL);
if (xa_is_err(curr))
return xa_err(curr);
if (curr == pasid_entry &&
new_domain == pasid_entry_to_domain(curr))
return 0;
[1] https://lore.kernel.org/linux-iommu/20250218195756.GG4183890@nvidia.com/
--
Regards,
Yi Liu
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v7 03/13] iommufd: Pass @pasid through the device attach/replace path
2025-02-16 3:52 [PATCH v7 00/13] iommufd support pasid attach/replace Yi Liu
2025-02-16 3:52 ` [PATCH v7 01/13] iommu: Add iommu_attach_device_pasid_handle() Yi Liu
2025-02-16 3:52 ` [PATCH v7 02/13] iommu: Introduce a replace API for device pasid Yi Liu
@ 2025-02-16 3:52 ` Yi Liu
2025-02-16 3:52 ` [PATCH v7 04/13] iommufd/device: Only add reserved_iova in non-pasid path Yi Liu
` (9 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Yi Liu @ 2025-02-16 3:52 UTC (permalink / raw)
To: joro, kevin.tian, baolu.lu, jgg
Cc: yi.l.liu, iommu, robin.murphy, nicolinc, will, vasant.hegde
Most of the core logic before conducting the actual device attach/
replace operation can be shared with pasid attach/replace. So pass
@pasid through the device attach/replace helpers to prepare adding
pasid attach/replace.
So far the @pasid should only be IOMMU_NO_PASID. No functional change.
Signed-off-by: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
---
drivers/iommu/iommufd/device.c | 67 +++++++++++++++----------
drivers/iommu/iommufd/hw_pagetable.c | 5 +-
drivers/iommu/iommufd/iommufd_private.h | 5 +-
3 files changed, 46 insertions(+), 31 deletions(-)
diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
index 0786290b4056..6ec7f6935115 100644
--- a/drivers/iommu/iommufd/device.c
+++ b/drivers/iommu/iommufd/device.c
@@ -355,7 +355,8 @@ iommufd_device_attach_reserved_iova(struct iommufd_device *idev,
/* The device attach/detach/replace helpers for attach_handle */
static int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt,
- struct iommufd_device *idev)
+ struct iommufd_device *idev,
+ ioasid_t pasid)
{
struct iommufd_attach_handle *handle;
int rc;
@@ -373,6 +374,7 @@ static int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt,
}
handle->idev = idev;
+ WARN_ON(pasid != IOMMU_NO_PASID);
rc = iommu_attach_group_handle(hwpt->domain, idev->igroup->group,
&handle->handle);
if (rc)
@@ -389,25 +391,28 @@ static int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt,
}
static struct iommufd_attach_handle *
-iommufd_device_get_attach_handle(struct iommufd_device *idev)
+iommufd_device_get_attach_handle(struct iommufd_device *idev, ioasid_t pasid)
{
struct iommu_attach_handle *handle;
lockdep_assert_held(&idev->igroup->lock);
handle =
- iommu_attach_handle_get(idev->igroup->group, IOMMU_NO_PASID, 0);
+ iommu_attach_handle_get(idev->igroup->group, pasid, 0);
if (IS_ERR(handle))
return NULL;
return to_iommufd_handle(handle);
}
static void iommufd_hwpt_detach_device(struct iommufd_hw_pagetable *hwpt,
- struct iommufd_device *idev)
+ struct iommufd_device *idev,
+ ioasid_t pasid)
{
struct iommufd_attach_handle *handle;
- handle = iommufd_device_get_attach_handle(idev);
+ WARN_ON(pasid != IOMMU_NO_PASID);
+
+ handle = iommufd_device_get_attach_handle(idev, pasid);
iommu_detach_group_handle(hwpt->domain, idev->igroup->group);
if (hwpt->fault) {
iommufd_auto_response_faults(hwpt, handle);
@@ -417,13 +422,17 @@ static void iommufd_hwpt_detach_device(struct iommufd_hw_pagetable *hwpt,
}
static int iommufd_hwpt_replace_device(struct iommufd_device *idev,
+ ioasid_t pasid,
struct iommufd_hw_pagetable *hwpt,
struct iommufd_hw_pagetable *old)
{
- struct iommufd_attach_handle *handle, *old_handle =
- iommufd_device_get_attach_handle(idev);
+ struct iommufd_attach_handle *handle, *old_handle;
int rc;
+ WARN_ON(pasid != IOMMU_NO_PASID);
+
+ old_handle = iommufd_device_get_attach_handle(idev, pasid);
+
handle = kzalloc(sizeof(*handle), GFP_KERNEL);
if (!handle)
return -ENOMEM;
@@ -458,7 +467,8 @@ static int iommufd_hwpt_replace_device(struct iommufd_device *idev,
}
int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
- struct iommufd_device *idev)
+ struct iommufd_device *idev,
+ ioasid_t pasid)
{
struct iommufd_hwpt_paging *hwpt_paging = find_hwpt_paging(hwpt);
int rc;
@@ -484,7 +494,7 @@ int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
* attachment.
*/
if (list_empty(&idev->igroup->device_list)) {
- rc = iommufd_hwpt_attach_device(hwpt, idev);
+ rc = iommufd_hwpt_attach_device(hwpt, idev, pasid);
if (rc)
goto err_unresv;
idev->igroup->hwpt = hwpt;
@@ -502,7 +512,7 @@ int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
}
struct iommufd_hw_pagetable *
-iommufd_hw_pagetable_detach(struct iommufd_device *idev)
+iommufd_hw_pagetable_detach(struct iommufd_device *idev, ioasid_t pasid)
{
struct iommufd_hw_pagetable *hwpt = idev->igroup->hwpt;
struct iommufd_hwpt_paging *hwpt_paging = find_hwpt_paging(hwpt);
@@ -510,7 +520,7 @@ iommufd_hw_pagetable_detach(struct iommufd_device *idev)
mutex_lock(&idev->igroup->lock);
list_del(&idev->group_item);
if (list_empty(&idev->igroup->device_list)) {
- iommufd_hwpt_detach_device(hwpt, idev);
+ iommufd_hwpt_detach_device(hwpt, idev, pasid);
idev->igroup->hwpt = NULL;
}
if (hwpt_paging)
@@ -522,12 +532,12 @@ iommufd_hw_pagetable_detach(struct iommufd_device *idev)
}
static struct iommufd_hw_pagetable *
-iommufd_device_do_attach(struct iommufd_device *idev,
+iommufd_device_do_attach(struct iommufd_device *idev, ioasid_t pasid,
struct iommufd_hw_pagetable *hwpt)
{
int rc;
- rc = iommufd_hw_pagetable_attach(hwpt, idev);
+ rc = iommufd_hw_pagetable_attach(hwpt, idev, pasid);
if (rc)
return ERR_PTR(rc);
return NULL;
@@ -576,7 +586,7 @@ iommufd_group_do_replace_reserved_iova(struct iommufd_group *igroup,
}
static struct iommufd_hw_pagetable *
-iommufd_device_do_replace(struct iommufd_device *idev,
+iommufd_device_do_replace(struct iommufd_device *idev, ioasid_t pasid,
struct iommufd_hw_pagetable *hwpt)
{
struct iommufd_hwpt_paging *hwpt_paging = find_hwpt_paging(hwpt);
@@ -605,7 +615,7 @@ iommufd_device_do_replace(struct iommufd_device *idev,
goto err_unlock;
}
- rc = iommufd_hwpt_replace_device(idev, hwpt, old_hwpt);
+ rc = iommufd_hwpt_replace_device(idev, pasid, hwpt, old_hwpt);
if (rc)
goto err_unresv;
@@ -638,7 +648,8 @@ iommufd_device_do_replace(struct iommufd_device *idev,
}
typedef struct iommufd_hw_pagetable *(*attach_fn)(
- struct iommufd_device *idev, struct iommufd_hw_pagetable *hwpt);
+ struct iommufd_device *idev, ioasid_t pasid,
+ struct iommufd_hw_pagetable *hwpt);
/*
* When automatically managing the domains we search for a compatible domain in
@@ -646,7 +657,7 @@ typedef struct iommufd_hw_pagetable *(*attach_fn)(
* Automatic domain selection will never pick a manually created domain.
*/
static struct iommufd_hw_pagetable *
-iommufd_device_auto_get_domain(struct iommufd_device *idev,
+iommufd_device_auto_get_domain(struct iommufd_device *idev, ioasid_t pasid,
struct iommufd_ioas *ioas, u32 *pt_id,
attach_fn do_attach)
{
@@ -675,7 +686,7 @@ iommufd_device_auto_get_domain(struct iommufd_device *idev,
hwpt = &hwpt_paging->common;
if (!iommufd_lock_obj(&hwpt->obj))
continue;
- destroy_hwpt = (*do_attach)(idev, hwpt);
+ destroy_hwpt = (*do_attach)(idev, pasid, hwpt);
if (IS_ERR(destroy_hwpt)) {
iommufd_put_object(idev->ictx, &hwpt->obj);
/*
@@ -702,7 +713,7 @@ iommufd_device_auto_get_domain(struct iommufd_device *idev,
hwpt = &hwpt_paging->common;
if (!immediate_attach) {
- destroy_hwpt = (*do_attach)(idev, hwpt);
+ destroy_hwpt = (*do_attach)(idev, pasid, hwpt);
if (IS_ERR(destroy_hwpt))
goto out_abort;
} else {
@@ -723,8 +734,9 @@ iommufd_device_auto_get_domain(struct iommufd_device *idev,
return destroy_hwpt;
}
-static int iommufd_device_change_pt(struct iommufd_device *idev, u32 *pt_id,
- attach_fn do_attach)
+static int iommufd_device_change_pt(struct iommufd_device *idev,
+ ioasid_t pasid,
+ u32 *pt_id, attach_fn do_attach)
{
struct iommufd_hw_pagetable *destroy_hwpt;
struct iommufd_object *pt_obj;
@@ -739,7 +751,7 @@ static int iommufd_device_change_pt(struct iommufd_device *idev, u32 *pt_id,
struct iommufd_hw_pagetable *hwpt =
container_of(pt_obj, struct iommufd_hw_pagetable, obj);
- destroy_hwpt = (*do_attach)(idev, hwpt);
+ destroy_hwpt = (*do_attach)(idev, pasid, hwpt);
if (IS_ERR(destroy_hwpt))
goto out_put_pt_obj;
break;
@@ -748,8 +760,8 @@ static int iommufd_device_change_pt(struct iommufd_device *idev, u32 *pt_id,
struct iommufd_ioas *ioas =
container_of(pt_obj, struct iommufd_ioas, obj);
- destroy_hwpt = iommufd_device_auto_get_domain(idev, ioas, pt_id,
- do_attach);
+ destroy_hwpt = iommufd_device_auto_get_domain(idev, pasid, ioas,
+ pt_id, do_attach);
if (IS_ERR(destroy_hwpt))
goto out_put_pt_obj;
break;
@@ -786,7 +798,8 @@ int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id)
{
int rc;
- rc = iommufd_device_change_pt(idev, pt_id, &iommufd_device_do_attach);
+ rc = iommufd_device_change_pt(idev, IOMMU_NO_PASID, pt_id,
+ &iommufd_device_do_attach);
if (rc)
return rc;
@@ -816,7 +829,7 @@ EXPORT_SYMBOL_NS_GPL(iommufd_device_attach, "IOMMUFD");
*/
int iommufd_device_replace(struct iommufd_device *idev, u32 *pt_id)
{
- return iommufd_device_change_pt(idev, pt_id,
+ return iommufd_device_change_pt(idev, IOMMU_NO_PASID, pt_id,
&iommufd_device_do_replace);
}
EXPORT_SYMBOL_NS_GPL(iommufd_device_replace, "IOMMUFD");
@@ -832,7 +845,7 @@ void iommufd_device_detach(struct iommufd_device *idev)
{
struct iommufd_hw_pagetable *hwpt;
- hwpt = iommufd_hw_pagetable_detach(idev);
+ hwpt = iommufd_hw_pagetable_detach(idev, IOMMU_NO_PASID);
iommufd_hw_pagetable_put(idev->ictx, hwpt);
refcount_dec(&idev->obj.users);
}
diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c
index 598be26a14e2..af2b72647d5a 100644
--- a/drivers/iommu/iommufd/hw_pagetable.c
+++ b/drivers/iommu/iommufd/hw_pagetable.c
@@ -184,7 +184,8 @@ iommufd_hwpt_paging_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas,
* sequence. Once those drivers are fixed this should be removed.
*/
if (immediate_attach) {
- rc = iommufd_hw_pagetable_attach(hwpt, idev);
+ /* Sinc this is just a trick, so passing IOMMU_NO_PASID is enough */
+ rc = iommufd_hw_pagetable_attach(hwpt, idev, IOMMU_NO_PASID);
if (rc)
goto out_abort;
}
@@ -197,7 +198,7 @@ iommufd_hwpt_paging_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas,
out_detach:
if (immediate_attach)
- iommufd_hw_pagetable_detach(idev);
+ iommufd_hw_pagetable_detach(idev, IOMMU_NO_PASID);
out_abort:
iommufd_object_abort_and_destroy(ictx, &hwpt->obj);
return ERR_PTR(rc);
diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index 8e0e3ab64747..193ee8a3d5f9 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -350,9 +350,10 @@ iommufd_hwpt_paging_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas,
bool immediate_attach,
const struct iommu_user_data *user_data);
int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
- struct iommufd_device *idev);
+ struct iommufd_device *idev,
+ ioasid_t pasid);
struct iommufd_hw_pagetable *
-iommufd_hw_pagetable_detach(struct iommufd_device *idev);
+iommufd_hw_pagetable_detach(struct iommufd_device *idev, ioasid_t pasid);
void iommufd_hwpt_paging_destroy(struct iommufd_object *obj);
void iommufd_hwpt_paging_abort(struct iommufd_object *obj);
void iommufd_hwpt_nested_destroy(struct iommufd_object *obj);
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v7 04/13] iommufd/device: Only add reserved_iova in non-pasid path
2025-02-16 3:52 [PATCH v7 00/13] iommufd support pasid attach/replace Yi Liu
` (2 preceding siblings ...)
2025-02-16 3:52 ` [PATCH v7 03/13] iommufd: Pass @pasid through the device attach/replace path Yi Liu
@ 2025-02-16 3:52 ` Yi Liu
2025-02-16 3:52 ` [PATCH v7 05/13] iommufd: Mark PASID-compatible domain Yi Liu
` (8 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Yi Liu @ 2025-02-16 3:52 UTC (permalink / raw)
To: joro, kevin.tian, baolu.lu, jgg
Cc: yi.l.liu, iommu, robin.murphy, nicolinc, will, vasant.hegde
As the pasid is passed through the attach/replace/detach helpers, it is
necessary to ensure only the non-pasid path adds reserved_iova.
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
---
drivers/iommu/iommufd/device.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
index 6ec7f6935115..72f6195e32f2 100644
--- a/drivers/iommu/iommufd/device.c
+++ b/drivers/iommu/iommufd/device.c
@@ -471,6 +471,7 @@ int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
ioasid_t pasid)
{
struct iommufd_hwpt_paging *hwpt_paging = find_hwpt_paging(hwpt);
+ bool add_reserved = !!(hwpt_paging && pasid == IOMMU_NO_PASID);
int rc;
mutex_lock(&idev->igroup->lock);
@@ -480,7 +481,7 @@ int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
goto err_unlock;
}
- if (hwpt_paging) {
+ if (add_reserved) {
rc = iommufd_device_attach_reserved_iova(idev, hwpt_paging);
if (rc)
goto err_unlock;
@@ -504,7 +505,7 @@ int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
mutex_unlock(&idev->igroup->lock);
return 0;
err_unresv:
- if (hwpt_paging)
+ if (add_reserved)
iopt_remove_reserved_iova(&hwpt_paging->ioas->iopt, idev->dev);
err_unlock:
mutex_unlock(&idev->igroup->lock);
@@ -523,7 +524,7 @@ iommufd_hw_pagetable_detach(struct iommufd_device *idev, ioasid_t pasid)
iommufd_hwpt_detach_device(hwpt, idev, pasid);
idev->igroup->hwpt = NULL;
}
- if (hwpt_paging)
+ if (hwpt_paging && pasid == IOMMU_NO_PASID)
iopt_remove_reserved_iova(&hwpt_paging->ioas->iopt, idev->dev);
mutex_unlock(&idev->igroup->lock);
@@ -590,6 +591,7 @@ iommufd_device_do_replace(struct iommufd_device *idev, ioasid_t pasid,
struct iommufd_hw_pagetable *hwpt)
{
struct iommufd_hwpt_paging *hwpt_paging = find_hwpt_paging(hwpt);
+ bool add_reserved = !!(hwpt_paging && pasid == IOMMU_NO_PASID);
struct iommufd_hwpt_paging *old_hwpt_paging;
struct iommufd_group *igroup = idev->igroup;
struct iommufd_hw_pagetable *old_hwpt;
@@ -609,7 +611,7 @@ iommufd_device_do_replace(struct iommufd_device *idev, ioasid_t pasid,
}
old_hwpt = igroup->hwpt;
- if (hwpt_paging) {
+ if (add_reserved) {
rc = iommufd_group_do_replace_reserved_iova(igroup, hwpt_paging);
if (rc)
goto err_unlock;
@@ -620,7 +622,7 @@ iommufd_device_do_replace(struct iommufd_device *idev, ioasid_t pasid,
goto err_unresv;
old_hwpt_paging = find_hwpt_paging(old_hwpt);
- if (old_hwpt_paging &&
+ if (old_hwpt_paging && pasid == IOMMU_NO_PASID &&
(!hwpt_paging || hwpt_paging->ioas != old_hwpt_paging->ioas))
iommufd_group_remove_reserved_iova(igroup, old_hwpt_paging);
@@ -640,7 +642,7 @@ iommufd_device_do_replace(struct iommufd_device *idev, ioasid_t pasid,
/* Caller must destroy old_hwpt */
return old_hwpt;
err_unresv:
- if (hwpt_paging)
+ if (add_reserved)
iommufd_group_remove_reserved_iova(igroup, hwpt_paging);
err_unlock:
mutex_unlock(&idev->igroup->lock);
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v7 05/13] iommufd: Mark PASID-compatible domain
2025-02-16 3:52 [PATCH v7 00/13] iommufd support pasid attach/replace Yi Liu
` (3 preceding siblings ...)
2025-02-16 3:52 ` [PATCH v7 04/13] iommufd/device: Only add reserved_iova in non-pasid path Yi Liu
@ 2025-02-16 3:52 ` Yi Liu
2025-02-16 3:52 ` [PATCH v7 06/13] iommufd: Support pasid attach/replace Yi Liu
` (7 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Yi Liu @ 2025-02-16 3:52 UTC (permalink / raw)
To: joro, kevin.tian, baolu.lu, jgg
Cc: yi.l.liu, iommu, robin.murphy, nicolinc, will, vasant.hegde
AMD IOMMU requires attaching PASID-compatible domains to PASID-capable
devices. This includes the domains attached to RID and PASIDs. Related
discussions in link [1] and [2]. ARM also has such a requirement, Intel
does not need it, but can live up with it. Hence, iommufd is going to
enforce this requirement as it is not harmful to vendors that do not
need it.
Mark the PASID-capable domains to prepare for adding this enforcement
when iommufd PASID support is added.
[1] https://lore.kernel.org/linux-iommu/20240709182303.GK14050@ziepe.ca/
[2] https://lore.kernel.org/linux-iommu/20240822124433.GD3468552@ziepe.ca/
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
---
drivers/iommu/iommufd/hw_pagetable.c | 3 +++
drivers/iommu/iommufd/iommufd_private.h | 1 +
2 files changed, 4 insertions(+)
diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c
index af2b72647d5a..6fc848d3ef47 100644
--- a/drivers/iommu/iommufd/hw_pagetable.c
+++ b/drivers/iommu/iommufd/hw_pagetable.c
@@ -132,6 +132,7 @@ iommufd_hwpt_paging_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas,
if (IS_ERR(hwpt_paging))
return ERR_CAST(hwpt_paging);
hwpt = &hwpt_paging->common;
+ hwpt->pasid_compat = flags & IOMMU_HWPT_ALLOC_PASID;
INIT_LIST_HEAD(&hwpt_paging->hwpt_item);
/* Pairs with iommufd_hw_pagetable_destroy() */
@@ -239,6 +240,7 @@ iommufd_hwpt_nested_alloc(struct iommufd_ctx *ictx,
if (IS_ERR(hwpt_nested))
return ERR_CAST(hwpt_nested);
hwpt = &hwpt_nested->common;
+ hwpt->pasid_compat = flags & IOMMU_HWPT_ALLOC_PASID;
refcount_inc(&parent->common.obj.users);
hwpt_nested->parent = parent;
@@ -293,6 +295,7 @@ iommufd_viommu_alloc_hwpt_nested(struct iommufd_viommu *viommu, u32 flags,
if (IS_ERR(hwpt_nested))
return ERR_CAST(hwpt_nested);
hwpt = &hwpt_nested->common;
+ hwpt->pasid_compat = flags & IOMMU_HWPT_ALLOC_PASID;
hwpt_nested->viommu = viommu;
refcount_inc(&viommu->obj.users);
diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index 193ee8a3d5f9..cfb6a0767a30 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -276,6 +276,7 @@ struct iommufd_hw_pagetable {
struct iommufd_object obj;
struct iommu_domain *domain;
struct iommufd_fault *fault;
+ bool pasid_compat : 1;
};
struct iommufd_hwpt_paging {
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v7 06/13] iommufd: Support pasid attach/replace
2025-02-16 3:52 [PATCH v7 00/13] iommufd support pasid attach/replace Yi Liu
` (4 preceding siblings ...)
2025-02-16 3:52 ` [PATCH v7 05/13] iommufd: Mark PASID-compatible domain Yi Liu
@ 2025-02-16 3:52 ` Yi Liu
2025-02-16 3:52 ` [PATCH v7 07/13] iommufd: Enforce PASID-compatible domain for RID Yi Liu
` (6 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Yi Liu @ 2025-02-16 3:52 UTC (permalink / raw)
To: joro, kevin.tian, baolu.lu, jgg
Cc: yi.l.liu, iommu, robin.murphy, nicolinc, will, vasant.hegde
This introduces three APIs for device drivers to manage pasid attach/
replace/detach.
int iommufd_device_pasid_attach(struct iommufd_device *idev,
ioasid_t pasid, u32 *pt_id);
int iommufd_device_pasid_replace(struct iommufd_device *idev,
ioasid_t pasid, u32 *pt_id);
void iommufd_device_pasid_detach(struct iommufd_device *idev,
ioasid_t pasid);
The pasid operations share underlying attach/replace/detach infrastructure
with the device operations, but still have some different implications:
- no reserved region per pasid otherwise SVA architecture is already
broken (CPU address space doesn't count device reserved regions);
- accordingly no sw_msi trick;
Cache coherency enforcement is still applied to pasid operations since
it is about memory accesses post page table walking (no matter the walk
is per RID or per PASID).
Since the attach is per PASID, this introduces a pasid_hwpts xarray to
track the per-pasid attach data.
AMD requires using PASID-compatible domains for PASIDs, hence the hwpts
directing the PASID path requires to flagged with IOMMU_HWPT_ALLOC_PASID.
Signed-off-by: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
---
drivers/iommu/iommufd/Makefile | 1 +
drivers/iommu/iommufd/device.c | 64 ++++++----
drivers/iommu/iommufd/iommufd_private.h | 26 ++++
drivers/iommu/iommufd/pasid.c | 155 ++++++++++++++++++++++++
include/linux/iommufd.h | 7 ++
5 files changed, 227 insertions(+), 26 deletions(-)
create mode 100644 drivers/iommu/iommufd/pasid.c
diff --git a/drivers/iommu/iommufd/Makefile b/drivers/iommu/iommufd/Makefile
index cb784da6cddc..a64a67b502ae 100644
--- a/drivers/iommu/iommufd/Makefile
+++ b/drivers/iommu/iommufd/Makefile
@@ -7,6 +7,7 @@ iommufd-y := \
ioas.o \
main.o \
pages.o \
+ pasid.o \
vfio_compat.o \
viommu.o
diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
index 72f6195e32f2..30dd2f79491a 100644
--- a/drivers/iommu/iommufd/device.c
+++ b/drivers/iommu/iommufd/device.c
@@ -136,6 +136,7 @@ void iommufd_device_destroy(struct iommufd_object *obj)
struct iommufd_device *idev =
container_of(obj, struct iommufd_device, obj);
+ WARN_ON(!xa_empty(&idev->pasid_hwpts));
iommu_device_release_dma_owner(idev->dev);
iommufd_put_group(idev->igroup);
if (!iommufd_selftest_is_mock_dev(idev->dev))
@@ -217,6 +218,8 @@ struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx,
idev->igroup = igroup;
mutex_init(&idev->iopf_lock);
+ xa_init(&idev->pasid_hwpts);
+
/*
* If the caller fails after this success it must call
* iommufd_unbind_device() which is safe since we hold this refcount.
@@ -354,15 +357,18 @@ iommufd_device_attach_reserved_iova(struct iommufd_device *idev,
/* The device attach/detach/replace helpers for attach_handle */
-static int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt,
- struct iommufd_device *idev,
- ioasid_t pasid)
+int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt,
+ struct iommufd_device *idev,
+ ioasid_t pasid)
{
struct iommufd_attach_handle *handle;
int rc;
lockdep_assert_held(&idev->igroup->lock);
+ if (pasid != IOMMU_NO_PASID && !hwpt->pasid_compat)
+ return -EINVAL;
+
handle = kzalloc(sizeof(*handle), GFP_KERNEL);
if (!handle)
return -ENOMEM;
@@ -374,9 +380,12 @@ static int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt,
}
handle->idev = idev;
- WARN_ON(pasid != IOMMU_NO_PASID);
- rc = iommu_attach_group_handle(hwpt->domain, idev->igroup->group,
- &handle->handle);
+ if (pasid == IOMMU_NO_PASID)
+ rc = iommu_attach_group_handle(hwpt->domain, idev->igroup->group,
+ &handle->handle);
+ else
+ rc = iommu_attach_device_pasid_handle(hwpt->domain, idev->dev,
+ pasid, &handle->handle);
if (rc)
goto out_disable_iopf;
@@ -404,16 +413,19 @@ iommufd_device_get_attach_handle(struct iommufd_device *idev, ioasid_t pasid)
return to_iommufd_handle(handle);
}
-static void iommufd_hwpt_detach_device(struct iommufd_hw_pagetable *hwpt,
- struct iommufd_device *idev,
- ioasid_t pasid)
+void iommufd_hwpt_detach_device(struct iommufd_hw_pagetable *hwpt,
+ struct iommufd_device *idev,
+ ioasid_t pasid)
{
struct iommufd_attach_handle *handle;
- WARN_ON(pasid != IOMMU_NO_PASID);
-
handle = iommufd_device_get_attach_handle(idev, pasid);
- iommu_detach_group_handle(hwpt->domain, idev->igroup->group);
+
+ if (pasid == IOMMU_NO_PASID)
+ iommu_detach_group_handle(hwpt->domain, idev->igroup->group);
+ else
+ iommu_detach_device_pasid(hwpt->domain, idev->dev, pasid);
+
if (hwpt->fault) {
iommufd_auto_response_faults(hwpt, handle);
iommufd_fault_iopf_disable(idev);
@@ -421,15 +433,16 @@ static void iommufd_hwpt_detach_device(struct iommufd_hw_pagetable *hwpt,
kfree(handle);
}
-static int iommufd_hwpt_replace_device(struct iommufd_device *idev,
- ioasid_t pasid,
- struct iommufd_hw_pagetable *hwpt,
- struct iommufd_hw_pagetable *old)
+int iommufd_hwpt_replace_device(struct iommufd_device *idev,
+ ioasid_t pasid,
+ struct iommufd_hw_pagetable *hwpt,
+ struct iommufd_hw_pagetable *old)
{
struct iommufd_attach_handle *handle, *old_handle;
int rc;
- WARN_ON(pasid != IOMMU_NO_PASID);
+ if (pasid != IOMMU_NO_PASID && !hwpt->pasid_compat)
+ return -EINVAL;
old_handle = iommufd_device_get_attach_handle(idev, pasid);
@@ -444,8 +457,12 @@ static int iommufd_hwpt_replace_device(struct iommufd_device *idev,
}
handle->idev = idev;
- rc = iommu_replace_group_handle(idev->igroup->group, hwpt->domain,
- &handle->handle);
+ if (pasid == IOMMU_NO_PASID)
+ rc = iommu_replace_group_handle(idev->igroup->group, hwpt->domain,
+ &handle->handle);
+ else
+ rc = iommu_replace_device_pasid_handle(hwpt->domain, idev->dev,
+ pasid, &handle->handle);
if (rc)
goto out_disable_iopf;
@@ -649,10 +666,6 @@ iommufd_device_do_replace(struct iommufd_device *idev, ioasid_t pasid,
return ERR_PTR(rc);
}
-typedef struct iommufd_hw_pagetable *(*attach_fn)(
- struct iommufd_device *idev, ioasid_t pasid,
- struct iommufd_hw_pagetable *hwpt);
-
/*
* When automatically managing the domains we search for a compatible domain in
* the iopt and if one is found use it, otherwise create a new domain.
@@ -736,9 +749,8 @@ iommufd_device_auto_get_domain(struct iommufd_device *idev, ioasid_t pasid,
return destroy_hwpt;
}
-static int iommufd_device_change_pt(struct iommufd_device *idev,
- ioasid_t pasid,
- u32 *pt_id, attach_fn do_attach)
+int iommufd_device_change_pt(struct iommufd_device *idev, ioasid_t pasid,
+ u32 *pt_id, attach_fn do_attach)
{
struct iommufd_hw_pagetable *destroy_hwpt;
struct iommufd_object *pt_obj;
diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index cfb6a0767a30..d533171d28de 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -400,6 +400,7 @@ struct iommufd_device {
struct list_head group_item;
/* always the physical device */
struct device *dev;
+ struct xarray pasid_hwpts;
bool enforce_cache_coherency;
/* protect iopf_enabled counter */
struct mutex iopf_lock;
@@ -417,6 +418,31 @@ iommufd_get_device(struct iommufd_ucmd *ucmd, u32 id)
void iommufd_device_destroy(struct iommufd_object *obj);
int iommufd_get_hw_info(struct iommufd_ucmd *ucmd);
+typedef struct iommufd_hw_pagetable *(*attach_fn)(
+ struct iommufd_device *idev, ioasid_t pasid,
+ struct iommufd_hw_pagetable *hwpt);
+
+int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt,
+ struct iommufd_device *idev,
+ ioasid_t pasid);
+void iommufd_hwpt_detach_device(struct iommufd_hw_pagetable *hwpt,
+ struct iommufd_device *idev,
+ ioasid_t pasid);
+int iommufd_hwpt_replace_device(struct iommufd_device *idev,
+ ioasid_t pasid,
+ struct iommufd_hw_pagetable *hwpt,
+ struct iommufd_hw_pagetable *old);
+
+int iommufd_device_change_pt(struct iommufd_device *idev, ioasid_t pasid,
+ u32 *pt_id, attach_fn do_attach);
+
+struct iommufd_hw_pagetable *
+iommufd_device_pasid_do_attach(struct iommufd_device *idev, ioasid_t pasid,
+ struct iommufd_hw_pagetable *hwpt);
+struct iommufd_hw_pagetable *
+iommufd_device_pasid_do_replace(struct iommufd_device *idev, ioasid_t pasid,
+ struct iommufd_hw_pagetable *hwpt);
+
struct iommufd_access {
struct iommufd_object obj;
struct iommufd_ctx *ictx;
diff --git a/drivers/iommu/iommufd/pasid.c b/drivers/iommu/iommufd/pasid.c
new file mode 100644
index 000000000000..4450c101e557
--- /dev/null
+++ b/drivers/iommu/iommufd/pasid.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2024, Intel Corporation
+ */
+#include <linux/iommufd.h>
+#include <linux/iommu.h>
+#include "../iommu-priv.h"
+
+#include "iommufd_private.h"
+
+struct iommufd_hw_pagetable *
+iommufd_device_pasid_do_attach(struct iommufd_device *idev, ioasid_t pasid,
+ struct iommufd_hw_pagetable *hwpt)
+{
+ void *curr;
+ int rc;
+
+ mutex_lock(&idev->igroup->lock);
+ curr = xa_cmpxchg(&idev->pasid_hwpts, pasid, NULL, hwpt, GFP_KERNEL);
+ if (curr) {
+ if (curr == hwpt)
+ rc = 0;
+ else
+ rc = xa_err(curr) ? : -EINVAL;
+ goto out_unlock;
+ }
+
+ rc = iommufd_hwpt_attach_device(hwpt, idev, pasid);
+ if (rc)
+ goto out_erase;
+
+ mutex_unlock(&idev->igroup->lock);
+ refcount_inc(&hwpt->obj.users);
+ return NULL;
+
+out_erase:
+ xa_erase(&idev->pasid_hwpts, pasid);
+out_unlock:
+ mutex_unlock(&idev->igroup->lock);
+ return rc ? ERR_PTR(rc) : NULL;
+}
+
+struct iommufd_hw_pagetable *
+iommufd_device_pasid_do_replace(struct iommufd_device *idev, ioasid_t pasid,
+ struct iommufd_hw_pagetable *hwpt)
+{
+ void *curr;
+ int rc;
+
+ mutex_lock(&idev->igroup->lock);
+ curr = xa_store(&idev->pasid_hwpts, pasid, hwpt, GFP_KERNEL);
+ rc = xa_err(curr);
+ if (rc)
+ goto out_unlock;
+
+ if (curr == hwpt)
+ goto out_unlock;
+
+ /* Not replace case */
+ if (!curr) {
+ xa_erase(&idev->pasid_hwpts, pasid);
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ /*
+ * After replacement, the reference on the old hwpt is retained
+ * in this thread as caller would free it.
+ */
+ rc = iommufd_hwpt_replace_device(idev, pasid, hwpt, curr);
+ if (rc) {
+ WARN_ON(xa_err(xa_store(&idev->pasid_hwpts, pasid,
+ curr, GFP_KERNEL)));
+ goto out_unlock;
+ }
+
+ mutex_unlock(&idev->igroup->lock);
+ refcount_inc(&hwpt->obj.users);
+ /* Caller must destroy old_hwpt */
+ return curr;
+
+out_unlock:
+ mutex_unlock(&idev->igroup->lock);
+ return rc ? ERR_PTR(rc) : NULL;
+}
+
+/**
+ * iommufd_device_pasid_attach - Connect a {device, pasid} to an iommu_domain
+ * @idev: device to attach
+ * @pasid: pasid to attach
+ * @pt_id: Input a IOMMUFD_OBJ_IOAS, or IOMMUFD_OBJ_HW_PAGETABLE
+ * Output the IOMMUFD_OBJ_HW_PAGETABLE ID
+ *
+ * This connects a pasid of the device to an iommu_domain. Once this
+ * completes the device could do DMA with the pasid.
+ *
+ * This function is undone by calling iommufd_device_detach_pasid().
+ *
+ * Return 0 for success, otherwise errno.
+ */
+int iommufd_device_pasid_attach(struct iommufd_device *idev,
+ ioasid_t pasid, u32 *pt_id)
+{
+ return iommufd_device_change_pt(idev, pasid, pt_id,
+ &iommufd_device_pasid_do_attach);
+}
+EXPORT_SYMBOL_NS_GPL(iommufd_device_pasid_attach, "IOMMUFD");
+
+/**
+ * iommufd_device_pasid_replace - Change the {device, pasid}'s iommu_domain
+ * @idev: device to change
+ * @pasid: pasid to change
+ * @pt_id: Input a IOMMUFD_OBJ_IOAS, or IOMMUFD_OBJ_HW_PAGETABLE
+ * Output the IOMMUFD_OBJ_HW_PAGETABLE ID
+ *
+ * This is the same as
+ * iommufd_device_pasid_detach();
+ * iommufd_device_pasid_attach();
+ *
+ * If it fails then no change is made to the attachment. The iommu driver may
+ * implement this so there is no disruption in translation. This can only be
+ * called if iommufd_device_pasid_attach() has already succeeded.
+ *
+ * Return 0 for success, otherwise errno.
+ */
+int iommufd_device_pasid_replace(struct iommufd_device *idev,
+ ioasid_t pasid, u32 *pt_id)
+{
+ return iommufd_device_change_pt(idev, pasid, pt_id,
+ &iommufd_device_pasid_do_replace);
+}
+EXPORT_SYMBOL_NS_GPL(iommufd_device_pasid_replace, "IOMMUFD");
+
+/**
+ * iommufd_device_pasid_detach - Disconnect a {device, pasid} to an iommu_domain
+ * @idev: device to detach
+ * @pasid: pasid to detach
+ *
+ * Undo iommufd_device_pasid_attach(). This disconnects the idev/pasid from
+ * the previously attached pt_id.
+ */
+void iommufd_device_pasid_detach(struct iommufd_device *idev, ioasid_t pasid)
+{
+ struct iommufd_hw_pagetable *hwpt;
+
+ mutex_lock(&idev->igroup->lock);
+ hwpt = xa_erase(&idev->pasid_hwpts, pasid);
+ if (WARN_ON(!hwpt)) {
+ mutex_unlock(&idev->igroup->lock);
+ return;
+ }
+ iommufd_hwpt_detach_device(hwpt, idev, pasid);
+ mutex_unlock(&idev->igroup->lock);
+ iommufd_hw_pagetable_put(idev->ictx, hwpt);
+}
+EXPORT_SYMBOL_NS_GPL(iommufd_device_pasid_detach, "IOMMUFD");
diff --git a/include/linux/iommufd.h b/include/linux/iommufd.h
index 11110c749200..af7e5a4bfcf2 100644
--- a/include/linux/iommufd.h
+++ b/include/linux/iommufd.h
@@ -8,6 +8,7 @@
#include <linux/err.h>
#include <linux/errno.h>
+#include <linux/iommu.h>
#include <linux/refcount.h>
#include <linux/types.h>
#include <linux/xarray.h>
@@ -56,6 +57,12 @@ int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id);
int iommufd_device_replace(struct iommufd_device *idev, u32 *pt_id);
void iommufd_device_detach(struct iommufd_device *idev);
+int iommufd_device_pasid_attach(struct iommufd_device *idev,
+ ioasid_t pasid, u32 *pt_id);
+int iommufd_device_pasid_replace(struct iommufd_device *idev,
+ ioasid_t pasid, u32 *pt_id);
+void iommufd_device_pasid_detach(struct iommufd_device *idev, ioasid_t pasid);
+
struct iommufd_ctx *iommufd_device_to_ictx(struct iommufd_device *idev);
u32 iommufd_device_to_id(struct iommufd_device *idev);
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v7 07/13] iommufd: Enforce PASID-compatible domain for RID
2025-02-16 3:52 [PATCH v7 00/13] iommufd support pasid attach/replace Yi Liu
` (5 preceding siblings ...)
2025-02-16 3:52 ` [PATCH v7 06/13] iommufd: Support pasid attach/replace Yi Liu
@ 2025-02-16 3:52 ` Yi Liu
2025-02-16 3:52 ` [PATCH v7 08/13] iommu/vt-d: Add IOMMU_HWPT_ALLOC_PASID support Yi Liu
` (5 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Yi Liu @ 2025-02-16 3:52 UTC (permalink / raw)
To: joro, kevin.tian, baolu.lu, jgg
Cc: yi.l.liu, iommu, robin.murphy, nicolinc, will, vasant.hegde
Per the definition of IOMMU_HWPT_ALLOC_PASID, iommufd needs to enforce
the RID to use PASID-compatible domain if PASID has been attached, and
vice versa. The PASID path has already enforced it. This adds the
enforcement in the RID path.
This enforcement requires a lock across the RID and PASID attach path,
the idev->igroup->lock is used as both the RID and the PASID path holds
it.
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
---
drivers/iommu/iommufd/device.c | 28 ++++++++++++++++++++++------
1 file changed, 22 insertions(+), 6 deletions(-)
diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
index 30dd2f79491a..e0f097b04467 100644
--- a/drivers/iommu/iommufd/device.c
+++ b/drivers/iommu/iommufd/device.c
@@ -357,6 +357,22 @@ iommufd_device_attach_reserved_iova(struct iommufd_device *idev,
/* The device attach/detach/replace helpers for attach_handle */
+static int iommufd_hwpt_pasid_compat(struct iommufd_hw_pagetable *hwpt,
+ struct iommufd_device *idev,
+ ioasid_t pasid)
+{
+ lockdep_assert_held(&idev->igroup->lock);
+
+ if (pasid == IOMMU_NO_PASID &&
+ !xa_empty(&idev->pasid_hwpts) && !hwpt->pasid_compat)
+ return -EINVAL;
+
+ if (pasid != IOMMU_NO_PASID &&
+ (!idev->igroup->hwpt->pasid_compat || !hwpt->pasid_compat))
+ return -EINVAL;
+ return 0;
+}
+
int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt,
struct iommufd_device *idev,
ioasid_t pasid)
@@ -364,10 +380,9 @@ int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt,
struct iommufd_attach_handle *handle;
int rc;
- lockdep_assert_held(&idev->igroup->lock);
-
- if (pasid != IOMMU_NO_PASID && !hwpt->pasid_compat)
- return -EINVAL;
+ rc = iommufd_hwpt_pasid_compat(hwpt, idev, pasid);
+ if (rc)
+ return rc;
handle = kzalloc(sizeof(*handle), GFP_KERNEL);
if (!handle)
@@ -441,8 +456,9 @@ int iommufd_hwpt_replace_device(struct iommufd_device *idev,
struct iommufd_attach_handle *handle, *old_handle;
int rc;
- if (pasid != IOMMU_NO_PASID && !hwpt->pasid_compat)
- return -EINVAL;
+ rc = iommufd_hwpt_pasid_compat(hwpt, idev, pasid);
+ if (rc)
+ return rc;
old_handle = iommufd_device_get_attach_handle(idev, pasid);
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v7 08/13] iommu/vt-d: Add IOMMU_HWPT_ALLOC_PASID support
2025-02-16 3:52 [PATCH v7 00/13] iommufd support pasid attach/replace Yi Liu
` (6 preceding siblings ...)
2025-02-16 3:52 ` [PATCH v7 07/13] iommufd: Enforce PASID-compatible domain for RID Yi Liu
@ 2025-02-16 3:52 ` Yi Liu
2025-02-16 3:52 ` [PATCH v7 09/13] iommufd: Allow allocating PASID-compatible domain Yi Liu
` (4 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Yi Liu @ 2025-02-16 3:52 UTC (permalink / raw)
To: joro, kevin.tian, baolu.lu, jgg
Cc: yi.l.liu, iommu, robin.murphy, nicolinc, will, vasant.hegde
Intel iommu driver just treats it as a nop since Intel VT-d does not have
special requirement on domains attached to either the PASID or RID of a
PASID-capable device.
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
---
drivers/iommu/intel/iommu.c | 3 ++-
drivers/iommu/intel/nested.c | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index cc46098f875b..7bc890609b90 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -3338,7 +3338,8 @@ intel_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags,
bool first_stage;
if (flags &
- (~(IOMMU_HWPT_ALLOC_NEST_PARENT | IOMMU_HWPT_ALLOC_DIRTY_TRACKING)))
+ (~(IOMMU_HWPT_ALLOC_NEST_PARENT | IOMMU_HWPT_ALLOC_DIRTY_TRACKING |
+ IOMMU_HWPT_ALLOC_PASID)))
return ERR_PTR(-EOPNOTSUPP);
if (nested_parent && !nested_supported(iommu))
return ERR_PTR(-EOPNOTSUPP);
diff --git a/drivers/iommu/intel/nested.c b/drivers/iommu/intel/nested.c
index aba92c00b427..6ac5c534bef4 100644
--- a/drivers/iommu/intel/nested.c
+++ b/drivers/iommu/intel/nested.c
@@ -198,7 +198,7 @@ intel_iommu_domain_alloc_nested(struct device *dev, struct iommu_domain *parent,
struct dmar_domain *domain;
int ret;
- if (!nested_supported(iommu) || flags)
+ if (!nested_supported(iommu) || flags & ~IOMMU_HWPT_ALLOC_PASID)
return ERR_PTR(-EOPNOTSUPP);
/* Must be nested domain */
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v7 09/13] iommufd: Allow allocating PASID-compatible domain
2025-02-16 3:52 [PATCH v7 00/13] iommufd support pasid attach/replace Yi Liu
` (7 preceding siblings ...)
2025-02-16 3:52 ` [PATCH v7 08/13] iommu/vt-d: Add IOMMU_HWPT_ALLOC_PASID support Yi Liu
@ 2025-02-16 3:52 ` Yi Liu
2025-02-16 3:52 ` [PATCH v7 10/13] iommufd/selftest: Add set_dev_pasid in mock iommu Yi Liu
` (3 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Yi Liu @ 2025-02-16 3:52 UTC (permalink / raw)
To: joro, kevin.tian, baolu.lu, jgg
Cc: yi.l.liu, iommu, robin.murphy, nicolinc, will, vasant.hegde
The underlying infrastructure has supported the PASID attach and related
enforcement per the requirement of the IOMMU_HWPT_ALLOC_PASID flag. This
extends iommufd to support PASID compatible domain requested by userspace
or the PASID compatible domain allocated in the auto_domain path.
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
---
drivers/iommu/iommufd/device.c | 4 +++-
drivers/iommu/iommufd/hw_pagetable.c | 7 ++++---
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
index e0f097b04467..afba66211b11 100644
--- a/drivers/iommu/iommufd/device.c
+++ b/drivers/iommu/iommufd/device.c
@@ -735,7 +735,9 @@ iommufd_device_auto_get_domain(struct iommufd_device *idev, ioasid_t pasid,
goto out_unlock;
}
- hwpt_paging = iommufd_hwpt_paging_alloc(idev->ictx, ioas, idev, 0,
+ hwpt_paging = iommufd_hwpt_paging_alloc(idev->ictx, ioas, idev,
+ pasid != IOMMU_NO_PASID ?
+ IOMMU_HWPT_ALLOC_PASID : 0,
immediate_attach, NULL);
if (IS_ERR(hwpt_paging)) {
destroy_hwpt = ERR_CAST(hwpt_paging);
diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c
index 6fc848d3ef47..7787d0931761 100644
--- a/drivers/iommu/iommufd/hw_pagetable.c
+++ b/drivers/iommu/iommufd/hw_pagetable.c
@@ -111,7 +111,8 @@ iommufd_hwpt_paging_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas,
{
const u32 valid_flags = IOMMU_HWPT_ALLOC_NEST_PARENT |
IOMMU_HWPT_ALLOC_DIRTY_TRACKING |
- IOMMU_HWPT_FAULT_ID_VALID;
+ IOMMU_HWPT_FAULT_ID_VALID |
+ IOMMU_HWPT_ALLOC_PASID;
const struct iommu_ops *ops = dev_iommu_ops(idev->dev);
struct iommufd_hwpt_paging *hwpt_paging;
struct iommufd_hw_pagetable *hwpt;
@@ -228,7 +229,7 @@ iommufd_hwpt_nested_alloc(struct iommufd_ctx *ictx,
struct iommufd_hw_pagetable *hwpt;
int rc;
- if ((flags & ~IOMMU_HWPT_FAULT_ID_VALID) ||
+ if ((flags & ~(IOMMU_HWPT_FAULT_ID_VALID | IOMMU_HWPT_ALLOC_PASID)) ||
!user_data->len || !ops->domain_alloc_nested)
return ERR_PTR(-EOPNOTSUPP);
if (parent->auto_domain || !parent->nest_parent ||
@@ -283,7 +284,7 @@ iommufd_viommu_alloc_hwpt_nested(struct iommufd_viommu *viommu, u32 flags,
struct iommufd_hw_pagetable *hwpt;
int rc;
- if (flags & ~IOMMU_HWPT_FAULT_ID_VALID)
+ if (flags & ~(IOMMU_HWPT_FAULT_ID_VALID | IOMMU_HWPT_ALLOC_PASID))
return ERR_PTR(-EOPNOTSUPP);
if (!user_data->len)
return ERR_PTR(-EOPNOTSUPP);
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v7 10/13] iommufd/selftest: Add set_dev_pasid in mock iommu
2025-02-16 3:52 [PATCH v7 00/13] iommufd support pasid attach/replace Yi Liu
` (8 preceding siblings ...)
2025-02-16 3:52 ` [PATCH v7 09/13] iommufd: Allow allocating PASID-compatible domain Yi Liu
@ 2025-02-16 3:52 ` Yi Liu
2025-02-16 3:52 ` [PATCH v7 11/13] iommufd/selftest: Add a helper to get test device Yi Liu
` (2 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Yi Liu @ 2025-02-16 3:52 UTC (permalink / raw)
To: joro, kevin.tian, baolu.lu, jgg
Cc: yi.l.liu, iommu, robin.murphy, nicolinc, will, vasant.hegde
The callback is needed to make pasid_attach/detach path complete for mock
device. A nop is enough for set_dev_pasid.
A MOCK_FLAGS_DEVICE_PASID is added to indicate a pasid-capable mock device
for the pasid test cases. Other test cases will still create a non-pasid
mock device. While the mock iommu always pretends to be pasid-capable.
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
---
drivers/iommu/iommufd/iommufd_test.h | 1 +
drivers/iommu/iommufd/selftest.c | 33 ++++++++++++++++++++++++----
2 files changed, 30 insertions(+), 4 deletions(-)
diff --git a/drivers/iommu/iommufd/iommufd_test.h b/drivers/iommu/iommufd/iommufd_test.h
index a6b7a163f636..bdc979557272 100644
--- a/drivers/iommu/iommufd/iommufd_test.h
+++ b/drivers/iommu/iommufd/iommufd_test.h
@@ -48,6 +48,7 @@ enum {
enum {
MOCK_FLAGS_DEVICE_NO_DIRTY = 1 << 0,
MOCK_FLAGS_DEVICE_HUGE_IOVA = 1 << 1,
+ MOCK_FLAGS_DEVICE_PASID = 1 << 2,
};
enum {
diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c
index d40deb0a4f06..69c17f545e70 100644
--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -200,8 +200,16 @@ static int mock_domain_nop_attach(struct iommu_domain *domain,
return 0;
}
+static int mock_domain_set_dev_pasid_nop(struct iommu_domain *domain,
+ struct device *dev, ioasid_t pasid,
+ struct iommu_domain *old)
+{
+ return 0;
+}
+
static const struct iommu_domain_ops mock_blocking_ops = {
.attach_dev = mock_domain_nop_attach,
+ .set_dev_pasid = mock_domain_set_dev_pasid_nop
};
static struct iommu_domain mock_blocking_domain = {
@@ -343,7 +351,7 @@ mock_domain_alloc_nested(struct device *dev, struct iommu_domain *parent,
struct mock_iommu_domain_nested *mock_nested;
struct mock_iommu_domain *mock_parent;
- if (flags)
+ if (flags & ~IOMMU_HWPT_ALLOC_PASID)
return ERR_PTR(-EOPNOTSUPP);
if (!parent || parent->ops != mock_ops.default_domain_ops)
return ERR_PTR(-EINVAL);
@@ -365,7 +373,8 @@ mock_domain_alloc_paging_flags(struct device *dev, u32 flags,
{
bool has_dirty_flag = flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
const u32 PAGING_FLAGS = IOMMU_HWPT_ALLOC_DIRTY_TRACKING |
- IOMMU_HWPT_ALLOC_NEST_PARENT;
+ IOMMU_HWPT_ALLOC_NEST_PARENT |
+ IOMMU_HWPT_ALLOC_PASID;
struct mock_dev *mdev = to_mock_dev(dev);
bool no_dirty_ops = mdev->flags & MOCK_FLAGS_DEVICE_NO_DIRTY;
struct mock_iommu_domain *mock;
@@ -585,7 +594,7 @@ mock_viommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
struct mock_viommu *mock_viommu = to_mock_viommu(viommu);
struct mock_iommu_domain_nested *mock_nested;
- if (flags)
+ if (flags & ~IOMMU_HWPT_ALLOC_PASID)
return ERR_PTR(-EOPNOTSUPP);
mock_nested = __mock_domain_alloc_nested(user_data);
@@ -720,6 +729,7 @@ static const struct iommu_ops mock_ops = {
.map_pages = mock_domain_map_pages,
.unmap_pages = mock_domain_unmap_pages,
.iova_to_phys = mock_domain_iova_to_phys,
+ .set_dev_pasid = mock_domain_set_dev_pasid_nop,
},
};
@@ -780,6 +790,7 @@ static struct iommu_domain_ops domain_nested_ops = {
.free = mock_domain_free_nested,
.attach_dev = mock_domain_nop_attach,
.cache_invalidate_user = mock_domain_cache_invalidate_user,
+ .set_dev_pasid = mock_domain_set_dev_pasid_nop,
};
static inline struct iommufd_hw_pagetable *
@@ -839,11 +850,16 @@ static void mock_dev_release(struct device *dev)
static struct mock_dev *mock_dev_create(unsigned long dev_flags)
{
+ struct property_entry prop[] = {
+ PROPERTY_ENTRY_U32("pasid-num-bits", 20),
+ {},
+ };
struct mock_dev *mdev;
int rc, i;
if (dev_flags &
- ~(MOCK_FLAGS_DEVICE_NO_DIRTY | MOCK_FLAGS_DEVICE_HUGE_IOVA))
+ ~(MOCK_FLAGS_DEVICE_NO_DIRTY |
+ MOCK_FLAGS_DEVICE_HUGE_IOVA | MOCK_FLAGS_DEVICE_PASID))
return ERR_PTR(-EINVAL);
mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
@@ -866,6 +882,14 @@ static struct mock_dev *mock_dev_create(unsigned long dev_flags)
if (rc)
goto err_put;
+ if (dev_flags & MOCK_FLAGS_DEVICE_PASID) {
+ rc = device_create_managed_software_node(&mdev->dev, prop, NULL);
+ if (rc) {
+ dev_err(&mdev->dev, "add pasid-num-bits property failed, rc: %d", rc);
+ goto err_put;
+ }
+ }
+
rc = device_add(&mdev->dev);
if (rc)
goto err_put;
@@ -1724,6 +1748,7 @@ int __init iommufd_test_init(void)
init_completion(&mock_iommu.complete);
mock_iommu_iopf_queue = iopf_queue_alloc("mock-iopfq");
+ mock_iommu.iommu_dev.max_pasids = (1 << 20);
return 0;
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v7 11/13] iommufd/selftest: Add a helper to get test device
2025-02-16 3:52 [PATCH v7 00/13] iommufd support pasid attach/replace Yi Liu
` (9 preceding siblings ...)
2025-02-16 3:52 ` [PATCH v7 10/13] iommufd/selftest: Add set_dev_pasid in mock iommu Yi Liu
@ 2025-02-16 3:52 ` Yi Liu
2025-02-16 3:52 ` [PATCH v7 12/13] iommufd/selftest: Add test ops to test pasid attach/detach Yi Liu
2025-02-16 3:52 ` [PATCH v7 13/13] iommufd/selftest: Add coverage for iommufd " Yi Liu
12 siblings, 0 replies; 17+ messages in thread
From: Yi Liu @ 2025-02-16 3:52 UTC (permalink / raw)
To: joro, kevin.tian, baolu.lu, jgg
Cc: yi.l.liu, iommu, robin.murphy, nicolinc, will, vasant.hegde
There is need to get the selftest device (sobj->type == TYPE_IDEV) in
multiple places, so have a helper to for it.
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
---
drivers/iommu/iommufd/selftest.c | 32 +++++++++++++++++++++-----------
1 file changed, 21 insertions(+), 11 deletions(-)
diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c
index 69c17f545e70..1c6cf66da411 100644
--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -970,29 +970,39 @@ static int iommufd_test_mock_domain(struct iommufd_ucmd *ucmd,
return rc;
}
-/* Replace the mock domain with a manually allocated hw_pagetable */
-static int iommufd_test_mock_domain_replace(struct iommufd_ucmd *ucmd,
- unsigned int device_id, u32 pt_id,
- struct iommu_test_cmd *cmd)
+static struct selftest_obj *
+iommufd_test_get_self_test_device(struct iommufd_ctx *ictx, u32 id)
{
struct iommufd_object *dev_obj;
struct selftest_obj *sobj;
- int rc;
/*
* Prefer to use the OBJ_SELFTEST because the destroy_rwsem will ensure
* it doesn't race with detach, which is not allowed.
*/
- dev_obj =
- iommufd_get_object(ucmd->ictx, device_id, IOMMUFD_OBJ_SELFTEST);
+ dev_obj = iommufd_get_object(ictx, id, IOMMUFD_OBJ_SELFTEST);
if (IS_ERR(dev_obj))
- return PTR_ERR(dev_obj);
+ return ERR_CAST(dev_obj);
sobj = to_selftest_obj(dev_obj);
if (sobj->type != TYPE_IDEV) {
- rc = -EINVAL;
- goto out_dev_obj;
+ iommufd_put_object(ictx, dev_obj);
+ return ERR_PTR(-EINVAL);
}
+ return sobj;
+}
+
+/* Replace the mock domain with a manually allocated hw_pagetable */
+static int iommufd_test_mock_domain_replace(struct iommufd_ucmd *ucmd,
+ unsigned int device_id, u32 pt_id,
+ struct iommu_test_cmd *cmd)
+{
+ struct selftest_obj *sobj;
+ int rc;
+
+ sobj = iommufd_test_get_self_test_device(ucmd->ictx, device_id);
+ if (IS_ERR(sobj))
+ return PTR_ERR(sobj);
rc = iommufd_device_replace(sobj->idev.idev, &pt_id);
if (rc)
@@ -1002,7 +1012,7 @@ static int iommufd_test_mock_domain_replace(struct iommufd_ucmd *ucmd,
rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
out_dev_obj:
- iommufd_put_object(ucmd->ictx, dev_obj);
+ iommufd_put_object(ucmd->ictx, &sobj->obj);
return rc;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v7 12/13] iommufd/selftest: Add test ops to test pasid attach/detach
2025-02-16 3:52 [PATCH v7 00/13] iommufd support pasid attach/replace Yi Liu
` (10 preceding siblings ...)
2025-02-16 3:52 ` [PATCH v7 11/13] iommufd/selftest: Add a helper to get test device Yi Liu
@ 2025-02-16 3:52 ` Yi Liu
2025-02-16 3:52 ` [PATCH v7 13/13] iommufd/selftest: Add coverage for iommufd " Yi Liu
12 siblings, 0 replies; 17+ messages in thread
From: Yi Liu @ 2025-02-16 3:52 UTC (permalink / raw)
To: joro, kevin.tian, baolu.lu, jgg
Cc: yi.l.liu, iommu, robin.murphy, nicolinc, will, vasant.hegde
This adds 4 test ops for pasid attach/replace/detach testing. There are
ops to attach/detach pasid, and also op to check the attached domain of
a pasid.
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
---
drivers/iommu/iommufd/iommufd_test.h | 30 ++++++
drivers/iommu/iommufd/selftest.c | 154 +++++++++++++++++++++++++++
2 files changed, 184 insertions(+)
diff --git a/drivers/iommu/iommufd/iommufd_test.h b/drivers/iommu/iommufd/iommufd_test.h
index bdc979557272..b3af7db97bc0 100644
--- a/drivers/iommu/iommufd/iommufd_test.h
+++ b/drivers/iommu/iommufd/iommufd_test.h
@@ -24,6 +24,10 @@ enum {
IOMMU_TEST_OP_MD_CHECK_IOTLB,
IOMMU_TEST_OP_TRIGGER_IOPF,
IOMMU_TEST_OP_DEV_CHECK_CACHE,
+ IOMMU_TEST_OP_PASID_ATTACH,
+ IOMMU_TEST_OP_PASID_REPLACE,
+ IOMMU_TEST_OP_PASID_DETACH,
+ IOMMU_TEST_OP_PASID_CHECK_DOMAIN,
};
enum {
@@ -146,6 +150,32 @@ struct iommu_test_cmd {
__u32 id;
__u32 cache;
} check_dev_cache;
+ struct {
+ __u32 pasid;
+ __u32 pt_id;
+ /* @id is stdev_id for IOMMU_TEST_OP_PASID_ATTACH
+ * pasid#1024 is for special test, avoid use it
+ * in normal case.
+ */
+ } pasid_attach;
+ struct {
+ __u32 pasid;
+ __u32 pt_id;
+ /* @id is stdev_id for IOMMU_TEST_OP_PASID_ATTACH
+ * pasid#1024 is for special test, avoid use it
+ * in normal case.
+ */
+ } pasid_replace;
+ struct {
+ __u32 pasid;
+ /* @id is stdev_id for IOMMU_TEST_OP_PASID_DETACH */
+ } pasid_detach;
+ struct {
+ __u32 pasid;
+ __u32 hwpt_id;
+ __u64 out_result_ptr;
+ /* @id is stdev_id for IOMMU_TEST_OP_HWPT_GET_DOMAIN */
+ } pasid_check;
};
__u32 last;
};
diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c
index 1c6cf66da411..f17964e2aa5f 100644
--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -200,10 +200,29 @@ static int mock_domain_nop_attach(struct iommu_domain *domain,
return 0;
}
+static bool pasid_1024_attached;
+
static int mock_domain_set_dev_pasid_nop(struct iommu_domain *domain,
struct device *dev, ioasid_t pasid,
struct iommu_domain *old)
{
+ /*
+ * First attach with pasid 1024 succ, second attach would fail.
+ * This is helpful to test the case in which the iommu core needs
+ * to rollback to old domain due to driver failure.
+ */
+ if (pasid == 1024) {
+ if (domain->type == IOMMU_DOMAIN_BLOCKED) {
+ pasid_1024_attached = false;
+ } else if (pasid_1024_attached) {
+ pasid_1024_attached = false;
+ // Fake an error to fail the replacement
+ return -ENOMEM;
+ } else {
+ pasid_1024_attached = true;
+ }
+ }
+
return 0;
}
@@ -1631,6 +1650,132 @@ static int iommufd_test_trigger_iopf(struct iommufd_ucmd *ucmd,
return 0;
}
+static int iommufd_test_pasid_attach(struct iommufd_ucmd *ucmd,
+ struct iommu_test_cmd *cmd)
+{
+ struct selftest_obj *sobj;
+ int rc;
+
+ sobj = iommufd_test_get_self_test_device(ucmd->ictx, cmd->id);
+ if (IS_ERR(sobj))
+ return PTR_ERR(sobj);
+
+ rc = iommufd_device_pasid_attach(sobj->idev.idev,
+ cmd->pasid_attach.pasid,
+ &cmd->pasid_attach.pt_id);
+ if (rc)
+ goto out_dev_obj;
+
+ rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
+ if (rc)
+ iommufd_device_pasid_detach(sobj->idev.idev,
+ cmd->pasid_attach.pasid);
+
+out_dev_obj:
+ iommufd_put_object(ucmd->ictx, &sobj->obj);
+ return rc;
+}
+
+static int iommufd_test_pasid_replace(struct iommufd_ucmd *ucmd,
+ struct iommu_test_cmd *cmd)
+{
+ struct selftest_obj *sobj;
+ int rc;
+
+ sobj = iommufd_test_get_self_test_device(ucmd->ictx, cmd->id);
+ if (IS_ERR(sobj))
+ return PTR_ERR(sobj);
+
+ rc = iommufd_device_pasid_replace(sobj->idev.idev,
+ cmd->pasid_attach.pasid,
+ &cmd->pasid_attach.pt_id);
+ if (rc)
+ goto out_dev_obj;
+
+ rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
+
+out_dev_obj:
+ iommufd_put_object(ucmd->ictx, &sobj->obj);
+ return rc;
+}
+
+static int iommufd_test_pasid_detach(struct iommufd_ucmd *ucmd,
+ struct iommu_test_cmd *cmd)
+{
+ struct selftest_obj *sobj;
+
+ sobj = iommufd_test_get_self_test_device(ucmd->ictx, cmd->id);
+ if (IS_ERR(sobj))
+ return PTR_ERR(sobj);
+
+ iommufd_device_pasid_detach(sobj->idev.idev,
+ cmd->pasid_detach.pasid);
+ iommufd_put_object(ucmd->ictx, &sobj->obj);
+ return 0;
+}
+
+static inline struct iommufd_hw_pagetable *
+iommufd_get_hwpt(struct iommufd_ucmd *ucmd, u32 id)
+{
+ struct iommufd_object *pt_obj;
+
+ pt_obj = iommufd_get_object(ucmd->ictx, id, IOMMUFD_OBJ_ANY);
+ if (IS_ERR(pt_obj))
+ return ERR_CAST(pt_obj);
+
+ if (pt_obj->type != IOMMUFD_OBJ_HWPT_NESTED &&
+ pt_obj->type != IOMMUFD_OBJ_HWPT_PAGING) {
+ iommufd_put_object(ucmd->ictx, pt_obj);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return container_of(pt_obj, struct iommufd_hw_pagetable, obj);
+}
+
+static int iommufd_test_pasid_check_domain(struct iommufd_ucmd *ucmd,
+ struct iommu_test_cmd *cmd)
+{
+ struct iommu_domain *attached_domain, *expect_domain = NULL;
+ struct iommufd_hw_pagetable *hwpt = NULL;
+ struct iommu_attach_handle *handle;
+ struct selftest_obj *sobj;
+ struct mock_dev *mdev;
+ bool result;
+ int rc = 0;
+
+ sobj = iommufd_test_get_self_test_device(ucmd->ictx, cmd->id);
+ if (IS_ERR(sobj))
+ return PTR_ERR(sobj);
+
+ mdev = sobj->idev.mock_dev;
+
+ handle = iommu_attach_handle_get(mdev->dev.iommu_group,
+ cmd->pasid_check.pasid, 0);
+ if (IS_ERR(handle))
+ attached_domain = NULL;
+ else
+ attached_domain = handle->domain;
+
+ if (cmd->pasid_check.hwpt_id) {
+ hwpt = iommufd_get_hwpt(ucmd, cmd->pasid_check.hwpt_id);
+ if (IS_ERR(hwpt)) {
+ rc = PTR_ERR(hwpt);
+ goto out_put_dev;
+ }
+ expect_domain = hwpt->domain;
+ }
+
+ result = (attached_domain == expect_domain) ? 1 : 0;
+ if (copy_to_user(u64_to_user_ptr(cmd->pasid_check.out_result_ptr),
+ &result, sizeof(result)))
+ rc = -EFAULT;
+ if (hwpt)
+ iommufd_put_object(ucmd->ictx, &hwpt->obj);
+out_put_dev:
+ iommufd_put_object(ucmd->ictx, &sobj->obj);
+ return rc;
+}
+
void iommufd_selftest_destroy(struct iommufd_object *obj)
{
struct selftest_obj *sobj = to_selftest_obj(obj);
@@ -1712,6 +1857,14 @@ int iommufd_test(struct iommufd_ucmd *ucmd)
cmd->dirty.flags);
case IOMMU_TEST_OP_TRIGGER_IOPF:
return iommufd_test_trigger_iopf(ucmd, cmd);
+ case IOMMU_TEST_OP_PASID_ATTACH:
+ return iommufd_test_pasid_attach(ucmd, cmd);
+ case IOMMU_TEST_OP_PASID_REPLACE:
+ return iommufd_test_pasid_replace(ucmd, cmd);
+ case IOMMU_TEST_OP_PASID_DETACH:
+ return iommufd_test_pasid_detach(ucmd, cmd);
+ case IOMMU_TEST_OP_PASID_CHECK_DOMAIN:
+ return iommufd_test_pasid_check_domain(ucmd, cmd);
default:
return -EOPNOTSUPP;
}
@@ -1759,6 +1912,7 @@ int __init iommufd_test_init(void)
mock_iommu_iopf_queue = iopf_queue_alloc("mock-iopfq");
mock_iommu.iommu_dev.max_pasids = (1 << 20);
+ pasid_1024_attached = false;
return 0;
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v7 13/13] iommufd/selftest: Add coverage for iommufd pasid attach/detach
2025-02-16 3:52 [PATCH v7 00/13] iommufd support pasid attach/replace Yi Liu
` (11 preceding siblings ...)
2025-02-16 3:52 ` [PATCH v7 12/13] iommufd/selftest: Add test ops to test pasid attach/detach Yi Liu
@ 2025-02-16 3:52 ` Yi Liu
12 siblings, 0 replies; 17+ messages in thread
From: Yi Liu @ 2025-02-16 3:52 UTC (permalink / raw)
To: joro, kevin.tian, baolu.lu, jgg
Cc: yi.l.liu, iommu, robin.murphy, nicolinc, will, vasant.hegde
This tests iommufd pasid attach/replace/detach.
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
---
tools/testing/selftests/iommu/iommufd.c | 348 ++++++++++++++++++
.../selftests/iommu/iommufd_fail_nth.c | 41 ++-
tools/testing/selftests/iommu/iommufd_utils.h | 102 +++++
3 files changed, 484 insertions(+), 7 deletions(-)
diff --git a/tools/testing/selftests/iommu/iommufd.c b/tools/testing/selftests/iommu/iommufd.c
index a1b2b657999d..575a9289dfdb 100644
--- a/tools/testing/selftests/iommu/iommufd.c
+++ b/tools/testing/selftests/iommu/iommufd.c
@@ -2956,4 +2956,352 @@ TEST_F(iommufd_viommu, vdevice_cache)
}
}
+FIXTURE(iommufd_device_pasid)
+{
+ int fd;
+ uint32_t ioas_id;
+ uint32_t hwpt_id;
+ uint32_t stdev_id;
+ uint32_t device_id;
+ uint32_t no_pasid_stdev_id;
+ uint32_t no_pasid_device_id;
+};
+
+FIXTURE_VARIANT(iommufd_device_pasid)
+{
+ bool pasid_capable;
+};
+
+FIXTURE_SETUP(iommufd_device_pasid)
+{
+ self->fd = open("/dev/iommu", O_RDWR);
+ ASSERT_NE(-1, self->fd);
+ test_ioctl_ioas_alloc(&self->ioas_id);
+
+ test_cmd_mock_domain_flags(self->ioas_id,
+ MOCK_FLAGS_DEVICE_PASID,
+ &self->stdev_id, &self->hwpt_id,
+ &self->device_id);
+ if (!variant->pasid_capable)
+ test_cmd_mock_domain_flags(self->ioas_id, 0,
+ &self->no_pasid_stdev_id, NULL,
+ &self->no_pasid_device_id);
+}
+
+FIXTURE_TEARDOWN(iommufd_device_pasid)
+{
+ teardown_iommufd(self->fd, _metadata);
+}
+
+FIXTURE_VARIANT_ADD(iommufd_device_pasid, no_pasid)
+{
+ .pasid_capable = false,
+};
+
+FIXTURE_VARIANT_ADD(iommufd_device_pasid, has_pasid)
+{
+ .pasid_capable = true,
+};
+
+TEST_F(iommufd_device_pasid, pasid_attach)
+{
+ struct iommu_hwpt_selftest data = {
+ .iotlb = IOMMU_TEST_IOTLB_DEFAULT,
+ };
+ uint32_t nested_hwpt_id[3] = {};
+ uint32_t parent_hwpt_id = 0;
+ uint32_t fault_id, fault_fd;
+ uint32_t s2_hwpt_id = 0;
+ uint32_t iopf_hwpt_id;
+ uint32_t pasid = 100;
+ uint32_t auto_hwpt;
+ uint32_t viommu_id;
+ bool result;
+
+ /* Allocate two nested hwpts sharing one common parent hwpt */
+ test_cmd_hwpt_alloc(self->device_id, self->ioas_id,
+ IOMMU_HWPT_ALLOC_NEST_PARENT,
+ &parent_hwpt_id);
+ test_cmd_hwpt_alloc_nested(self->device_id, parent_hwpt_id,
+ IOMMU_HWPT_ALLOC_PASID,
+ &nested_hwpt_id[0],
+ IOMMU_HWPT_DATA_SELFTEST,
+ &data, sizeof(data));
+ test_cmd_hwpt_alloc_nested(self->device_id, parent_hwpt_id,
+ IOMMU_HWPT_ALLOC_PASID,
+ &nested_hwpt_id[1],
+ IOMMU_HWPT_DATA_SELFTEST,
+ &data, sizeof(data));
+
+ /* Faulte related preparation */
+ test_ioctl_fault_alloc(&fault_id, &fault_fd);
+ test_cmd_hwpt_alloc_iopf(self->device_id, parent_hwpt_id, fault_id,
+ IOMMU_HWPT_FAULT_ID_VALID | IOMMU_HWPT_ALLOC_PASID,
+ &iopf_hwpt_id,
+ IOMMU_HWPT_DATA_SELFTEST, &data,
+ sizeof(data));
+
+ /* Allocate a regular nested hwpt based on viommu */
+ test_cmd_viommu_alloc(self->device_id, parent_hwpt_id,
+ IOMMU_VIOMMU_TYPE_SELFTEST,
+ &viommu_id);
+ test_cmd_hwpt_alloc_nested(self->device_id, viommu_id,
+ IOMMU_HWPT_ALLOC_PASID,
+ &nested_hwpt_id[2],
+ IOMMU_HWPT_DATA_SELFTEST, &data,
+ sizeof(data));
+
+ test_cmd_hwpt_alloc(self->device_id, self->ioas_id,
+ IOMMU_HWPT_ALLOC_PASID,
+ &s2_hwpt_id);
+
+ /* Attach RID to non-pasid compat domain, */
+ test_cmd_mock_domain_replace(self->stdev_id, parent_hwpt_id);
+ /* then attach to pasid should fail */
+ test_err_pasid_attach(EINVAL, pasid, s2_hwpt_id, NULL);
+
+ /* Attach RID to pasid compat domain, */
+ test_cmd_mock_domain_replace(self->stdev_id, s2_hwpt_id);
+ /* then attach to pasid should succeed, */
+ test_cmd_pasid_attach(pasid, nested_hwpt_id[0], NULL);
+ /* but attach RID to non-pasid compat domain should fail now. */
+ test_err_mock_domain_replace(EINVAL, self->stdev_id, parent_hwpt_id);
+ test_cmd_pasid_detach(pasid);
+
+ if (!variant->pasid_capable) {
+ /*
+ * PASID-compatible domain can be used by non-PASID-capable
+ * device.
+ */
+ test_cmd_mock_domain_replace(self->no_pasid_stdev_id, nested_hwpt_id[0]);
+ test_cmd_mock_domain_replace(self->no_pasid_stdev_id, self->ioas_id);
+ /*
+ * Attach hwpt to pasid#100 of non-PASID-capable device,
+ * should fail, no matter domain is pasid-comapt or not.
+ */
+ EXPECT_ERRNO(EINVAL,
+ _test_cmd_pasid_attach(self->fd, self->no_pasid_stdev_id,
+ pasid, parent_hwpt_id, NULL));
+ EXPECT_ERRNO(EINVAL,
+ _test_cmd_pasid_attach(self->fd, self->no_pasid_stdev_id,
+ pasid, s2_hwpt_id, NULL));
+ }
+
+ /*
+ * Attach non pasid compat hwpt to pasid-capable device, should
+ * fail, and have null domain.
+ */
+ test_err_pasid_attach(EINVAL, pasid, parent_hwpt_id, NULL);
+ ASSERT_EQ(0,
+ test_cmd_pasid_check_domain(self->fd, self->stdev_id,
+ pasid, 0, &result));
+ EXPECT_EQ(1, result);
+
+ /*
+ * Attach ioas to pasid 100, should succeed, domain should
+ * be valid.
+ */
+ test_cmd_pasid_attach(pasid, self->ioas_id, &auto_hwpt);
+ ASSERT_EQ(0,
+ test_cmd_pasid_check_domain(self->fd, self->stdev_id,
+ pasid, auto_hwpt, &result));
+ EXPECT_EQ(1, result);
+
+ /*
+ * Attach same ioas to pasid 100, should succeed.
+ */
+ test_cmd_pasid_attach(pasid, self->ioas_id, &auto_hwpt);
+
+ /*
+ * Try attach pasid 100 with another hwpt, should FAIL
+ * as attach does not allow overwrite, use REPLACE instead.
+ */
+ test_err_pasid_attach(EINVAL, pasid, nested_hwpt_id[0], NULL);
+
+ /*
+ * Detach hwpt from pasid 100, and check if the pasid 100
+ * has null domain. Should be done before the next attach.
+ */
+ test_cmd_pasid_detach(pasid);
+ ASSERT_EQ(0,
+ test_cmd_pasid_check_domain(self->fd, self->stdev_id,
+ pasid, 0, &result));
+ EXPECT_EQ(1, result);
+
+ /*
+ * Attach nested hwpt to pasid 100, should succeed, domain
+ * should be valid.
+ */
+ test_cmd_pasid_attach(pasid, nested_hwpt_id[0], NULL);
+ ASSERT_EQ(0,
+ test_cmd_pasid_check_domain(self->fd, self->stdev_id,
+ pasid, nested_hwpt_id[0],
+ &result));
+ EXPECT_EQ(1, result);
+
+ /*
+ * Try attach pasid 100 to same nested_hwpt_id[0], should succeed.
+ */
+ test_cmd_pasid_attach(pasid, nested_hwpt_id[0], NULL);
+
+ /*
+ * Detach hwpt from pasid 100, and check if the pasid 100
+ * has null domain
+ */
+ test_cmd_pasid_detach(pasid);
+ ASSERT_EQ(0,
+ test_cmd_pasid_check_domain(self->fd, self->stdev_id,
+ pasid, 0, &result));
+ EXPECT_EQ(1, result);
+
+ /* Replace tests */
+
+ pasid = 200;
+ /*
+ * Replace pasid 200 without attaching it first, should
+ * fail with -EINVAL.
+ */
+ test_err_cmd_pasid_replace(EINVAL, pasid, s2_hwpt_id, NULL);
+
+ /*
+ * Attach a s2 hwpt to pasid 200, should succeed, domain should
+ * be valid.
+ */
+ test_cmd_pasid_attach(pasid, s2_hwpt_id, NULL);
+ ASSERT_EQ(0,
+ test_cmd_pasid_check_domain(self->fd, self->stdev_id,
+ pasid, s2_hwpt_id,
+ &result));
+ EXPECT_EQ(1, result);
+
+ /*
+ * Replace pasid 200 with self->ioas_id, should succeed,
+ * and have valid domain.
+ */
+ test_cmd_pasid_replace(pasid, self->ioas_id, &auto_hwpt);
+ ASSERT_EQ(0,
+ test_cmd_pasid_check_domain(self->fd, self->stdev_id,
+ pasid, auto_hwpt,
+ &result));
+ EXPECT_EQ(1, result);
+
+ /*
+ * Replace a nested hwpt for pasid 200, should succeed,
+ * and have valid domain.
+ */
+ test_cmd_pasid_replace(pasid, nested_hwpt_id[0], NULL);
+ ASSERT_EQ(0,
+ test_cmd_pasid_check_domain(self->fd, self->stdev_id,
+ pasid, nested_hwpt_id[0],
+ &result));
+ EXPECT_EQ(1, result);
+
+ /*
+ * Replace with another nested hwpt for pasid 200, should
+ * succeed, and have valid domain.
+ */
+ test_cmd_pasid_replace(pasid, nested_hwpt_id[1], NULL);
+ ASSERT_EQ(0,
+ test_cmd_pasid_check_domain(self->fd, self->stdev_id,
+ pasid, nested_hwpt_id[1],
+ &result));
+ EXPECT_EQ(1, result);
+
+ /*
+ * Detach hwpt from pasid 200, and check if the pasid 200
+ * has null domain.
+ */
+ test_cmd_pasid_detach(pasid);
+ ASSERT_EQ(0,
+ test_cmd_pasid_check_domain(self->fd, self->stdev_id,
+ pasid, 0, &result));
+ EXPECT_EQ(1, result);
+
+ /* Negative Tests for pasid replace, use pasid 1024 */
+
+ /*
+ * Attach a s2 hwpt to pasid 1024, should succeed, domain should
+ * be valid.
+ */
+ pasid = 1024;
+ test_cmd_pasid_attach(pasid, s2_hwpt_id, NULL);
+ ASSERT_EQ(0,
+ test_cmd_pasid_check_domain(self->fd, self->stdev_id,
+ pasid, s2_hwpt_id,
+ &result));
+ EXPECT_EQ(1, result);
+
+ /*
+ * Replace pasid 1024 with self->ioas_id, should fail,
+ * but have the old valid domain. This is a designed
+ * negative case, normally replace with self->ioas_id
+ * could succeed.
+ */
+ test_err_cmd_pasid_replace(ENOMEM, pasid, self->ioas_id, NULL);
+ ASSERT_EQ(0,
+ test_cmd_pasid_check_domain(self->fd, self->stdev_id,
+ pasid, s2_hwpt_id,
+ &result));
+ EXPECT_EQ(1, result);
+
+ /*
+ * Detach hwpt from pasid 1024, and check if the pasid 1024
+ * has null domain.
+ */
+ test_cmd_pasid_detach(pasid);
+ ASSERT_EQ(0,
+ test_cmd_pasid_check_domain(self->fd, self->stdev_id,
+ pasid, 0, &result));
+ EXPECT_EQ(1, result);
+
+ /* Attach to iopf-capable hwpt */
+
+ /*
+ * Attach an iopf hwpt to pasid 2048, should succeed, domain should
+ * be valid.
+ */
+ pasid = 2048;
+ test_cmd_pasid_attach(pasid, iopf_hwpt_id, NULL);
+ ASSERT_EQ(0,
+ test_cmd_pasid_check_domain(self->fd, self->stdev_id,
+ pasid, iopf_hwpt_id,
+ &result));
+ EXPECT_EQ(1, result);
+
+ /*
+ * Replace with s2_hwpt_id for pasid 2048, should
+ * succeed, and have valid domain.
+ */
+ test_cmd_pasid_replace(pasid, s2_hwpt_id, NULL);
+ ASSERT_EQ(0,
+ test_cmd_pasid_check_domain(self->fd, self->stdev_id,
+ pasid, s2_hwpt_id,
+ &result));
+ EXPECT_EQ(1, result);
+
+ /*
+ * Detach hwpt from pasid 2048, and check if the pasid 2048
+ * has null domain.
+ */
+ test_cmd_pasid_detach(pasid);
+ ASSERT_EQ(0,
+ test_cmd_pasid_check_domain(self->fd, self->stdev_id,
+ pasid, 0, &result));
+ EXPECT_EQ(1, result);
+
+ test_ioctl_destroy(iopf_hwpt_id);
+ close(fault_fd);
+ test_ioctl_destroy(fault_id);
+
+ /* Detach the s2_hwpt_id from RID */
+ test_cmd_mock_domain_replace(self->stdev_id, self->ioas_id);
+
+ test_ioctl_destroy(nested_hwpt_id[0]);
+ test_ioctl_destroy(nested_hwpt_id[1]);
+ test_ioctl_destroy(nested_hwpt_id[2]);
+ test_ioctl_destroy(viommu_id);
+ test_ioctl_destroy(parent_hwpt_id);
+ test_ioctl_destroy(s2_hwpt_id);
+}
+
TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/iommu/iommufd_fail_nth.c b/tools/testing/selftests/iommu/iommufd_fail_nth.c
index 64b1f8e1b0cf..6bbdc187a986 100644
--- a/tools/testing/selftests/iommu/iommufd_fail_nth.c
+++ b/tools/testing/selftests/iommu/iommufd_fail_nth.c
@@ -209,12 +209,16 @@ FIXTURE(basic_fail_nth)
{
int fd;
uint32_t access_id;
+ uint32_t stdev_id;
+ uint32_t pasid;
};
FIXTURE_SETUP(basic_fail_nth)
{
self->fd = -1;
self->access_id = 0;
+ self->stdev_id = 0;
+ self->pasid = 0; //test should use a non-zero value
}
FIXTURE_TEARDOWN(basic_fail_nth)
@@ -226,6 +230,8 @@ FIXTURE_TEARDOWN(basic_fail_nth)
rc = _test_cmd_destroy_access(self->access_id);
assert(rc == 0);
}
+ if (self->pasid && self->stdev_id)
+ _test_cmd_pasid_detach(self->fd, self->stdev_id, self->pasid);
teardown_iommufd(self->fd, _metadata);
}
@@ -623,7 +629,6 @@ TEST_FAIL_NTH(basic_fail_nth, device)
uint32_t fault_hwpt_id;
uint32_t ioas_id;
uint32_t ioas_id2;
- uint32_t stdev_id;
uint32_t idev_id;
uint32_t hwpt_id;
uint32_t viommu_id;
@@ -654,25 +659,29 @@ TEST_FAIL_NTH(basic_fail_nth, device)
fail_nth_enable();
- if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, NULL,
- &idev_id))
+ if (_test_cmd_mock_domain_flags(self->fd, ioas_id,
+ MOCK_FLAGS_DEVICE_PASID,
+ &self->stdev_id, NULL, &idev_id))
return -1;
if (_test_cmd_get_hw_info(self->fd, idev_id, &info, sizeof(info), NULL))
return -1;
- if (_test_cmd_hwpt_alloc(self->fd, idev_id, ioas_id, 0, 0, &hwpt_id,
+ if (_test_cmd_hwpt_alloc(self->fd, idev_id, ioas_id, 0,
+ IOMMU_HWPT_ALLOC_PASID, &hwpt_id,
IOMMU_HWPT_DATA_NONE, 0, 0))
return -1;
- if (_test_cmd_mock_domain_replace(self->fd, stdev_id, ioas_id2, NULL))
+ if (_test_cmd_mock_domain_replace(self->fd, self->stdev_id, ioas_id2, NULL))
return -1;
- if (_test_cmd_mock_domain_replace(self->fd, stdev_id, hwpt_id, NULL))
+ if (_test_cmd_mock_domain_replace(self->fd, self->stdev_id, hwpt_id, NULL))
return -1;
if (_test_cmd_hwpt_alloc(self->fd, idev_id, ioas_id, 0,
- IOMMU_HWPT_ALLOC_NEST_PARENT, &hwpt_id,
+ IOMMU_HWPT_ALLOC_NEST_PARENT |
+ IOMMU_HWPT_ALLOC_PASID,
+ &hwpt_id,
IOMMU_HWPT_DATA_NONE, 0, 0))
return -1;
@@ -692,6 +701,24 @@ TEST_FAIL_NTH(basic_fail_nth, device)
IOMMU_HWPT_DATA_SELFTEST, &data, sizeof(data)))
return -1;
+ self->pasid = 200;
+
+ /* Tests for pasid attach/replace/detach */
+ if (_test_cmd_pasid_attach(self->fd, self->stdev_id,
+ self->pasid, ioas_id, NULL)) {
+ self->pasid = 0;
+ return -1;
+ }
+
+ if (_test_cmd_pasid_replace(self->fd, self->stdev_id,
+ self->pasid, ioas_id2, NULL))
+ return -1;
+
+ if (_test_cmd_pasid_detach(self->fd, self->stdev_id, self->pasid))
+ return -1;
+
+ self->pasid = 0;
+
return 0;
}
diff --git a/tools/testing/selftests/iommu/iommufd_utils.h b/tools/testing/selftests/iommu/iommufd_utils.h
index d979f5b0efe8..523ff28e4bc9 100644
--- a/tools/testing/selftests/iommu/iommufd_utils.h
+++ b/tools/testing/selftests/iommu/iommufd_utils.h
@@ -936,3 +936,105 @@ static int _test_cmd_vdevice_alloc(int fd, __u32 viommu_id, __u32 idev_id,
EXPECT_ERRNO(_errno, \
_test_cmd_vdevice_alloc(self->fd, viommu_id, idev_id, \
virt_id, vdev_id))
+
+static int _test_cmd_pasid_attach(int fd, __u32 stdev_id, __u32 pasid,
+ __u32 pt_id, __u32 *out_pt_id)
+{
+ struct iommu_test_cmd test_attach = {
+ .size = sizeof(test_attach),
+ .op = IOMMU_TEST_OP_PASID_ATTACH,
+ .id = stdev_id,
+ .pasid_attach = {
+ .pasid = pasid,
+ .pt_id = pt_id,
+ },
+ };
+ int ret;
+
+ ret = ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_PASID_ATTACH),
+ &test_attach);
+ if (ret)
+ return ret;
+
+ if (out_pt_id)
+ *out_pt_id = test_attach.pasid_attach.pt_id;
+ return 0;
+}
+
+#define test_cmd_pasid_attach(pasid, hwpt_id, out_pt_id) \
+ ASSERT_EQ(0, _test_cmd_pasid_attach(self->fd, self->stdev_id, \
+ pasid, hwpt_id, out_pt_id))
+
+#define test_err_pasid_attach(_errno, pasid, hwpt_id, out_pt_id) \
+ EXPECT_ERRNO(_errno, \
+ _test_cmd_pasid_attach(self->fd, self->stdev_id, \
+ pasid, hwpt_id, out_pt_id))
+
+static int _test_cmd_pasid_replace(int fd, __u32 stdev_id, __u32 pasid,
+ __u32 pt_id, __u32 *out_pt_id)
+{
+ struct iommu_test_cmd test_replace = {
+ .size = sizeof(test_replace),
+ .op = IOMMU_TEST_OP_PASID_REPLACE,
+ .id = stdev_id,
+ .pasid_replace = {
+ .pasid = pasid,
+ .pt_id = pt_id,
+ },
+ };
+ int ret;
+
+ ret = ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_PASID_REPLACE),
+ &test_replace);
+ if (ret)
+ return ret;
+
+ if (out_pt_id)
+ *out_pt_id = test_replace.pasid_replace.pt_id;
+ return 0;
+}
+
+#define test_cmd_pasid_replace(pasid, hwpt_id, out_pt_id) \
+ ASSERT_EQ(0, _test_cmd_pasid_replace(self->fd, self->stdev_id, \
+ pasid, hwpt_id, out_pt_id))
+
+#define test_err_cmd_pasid_replace(_errno, pasid, hwpt_id, out_pt_id) \
+ EXPECT_ERRNO(_errno, \
+ _test_cmd_pasid_replace(self->fd, self->stdev_id, \
+ pasid, hwpt_id, out_pt_id))
+
+static int _test_cmd_pasid_detach(int fd, __u32 stdev_id, __u32 pasid)
+{
+ struct iommu_test_cmd test_detach = {
+ .size = sizeof(test_detach),
+ .op = IOMMU_TEST_OP_PASID_DETACH,
+ .id = stdev_id,
+ .pasid_detach = {
+ .pasid = pasid,
+ },
+ };
+
+ return ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_PASID_DETACH),
+ &test_detach);
+}
+
+#define test_cmd_pasid_detach(pasid) \
+ ASSERT_EQ(0, _test_cmd_pasid_detach(self->fd, self->stdev_id, pasid))
+
+static int test_cmd_pasid_check_domain(int fd, __u32 stdev_id, __u32 pasid,
+ __u32 hwpt_id, bool *result)
+{
+ struct iommu_test_cmd test_pasid_check = {
+ .size = sizeof(test_pasid_check),
+ .op = IOMMU_TEST_OP_PASID_CHECK_DOMAIN,
+ .id = stdev_id,
+ .pasid_check = {
+ .pasid = pasid,
+ .hwpt_id = hwpt_id,
+ .out_result_ptr = (__u64)result,
+ },
+ };
+
+ return ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_PASID_CHECK_DOMAIN),
+ &test_pasid_check);
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread