Previously, the domain that a page fault targets is stored in an
iopf_group, which represents a minimal set of page faults. With the
introduction of attachment handle, replace the domain with the handle
so that the fault handler can obtain more information as needed
when handling the faults.
iommu_report_device_fault() is currently used for SVA page faults,
which handles the page fault in an internal cycle. The domain is retrieved
with iommu_get_domain_for_dev_pasid() if the pasid in the fault message
is valid. This doesn't work in IOMMUFD case, where if the pasid table of
a device is wholly managed by user space, there is no domain attached to
the PASID of the device. Improve this code to try fetching the attachment
handle on a PASID if possible, otherwise try it on the RID.
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
include/linux/iommu.h | 1 +
drivers/iommu/io-pgfault.c | 34 ++++++++++++++--------------------
2 files changed, 15 insertions(+), 20 deletions(-)
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index be9c9a10169d..4ff68d50ae39 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -128,6 +128,7 @@ struct iopf_group {
struct list_head pending_node;
struct work_struct work;
struct iommu_domain *domain;
+ struct iommu_attach_handle *attach_handle;
/* The device's fault data parameter. */
struct iommu_fault_param *fault_param;
};
diff --git a/drivers/iommu/io-pgfault.c b/drivers/iommu/io-pgfault.c
index 06d78fcc79fd..f19215d4ffa2 100644
--- a/drivers/iommu/io-pgfault.c
+++ b/drivers/iommu/io-pgfault.c
@@ -59,28 +59,19 @@ void iopf_free_group(struct iopf_group *group)
}
EXPORT_SYMBOL_GPL(iopf_free_group);
-static struct iommu_domain *get_domain_for_iopf(struct device *dev,
- struct iommu_fault *fault)
+static int get_attach_handle_for_iopf(struct device *dev, ioasid_t pasid,
+ struct iopf_group *group)
{
- struct iommu_domain *domain;
+ struct iommu_attach_handle *handle;
- if (fault->prm.flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID) {
- domain = iommu_get_domain_for_dev_pasid(dev, fault->prm.pasid, 0);
- if (IS_ERR(domain))
- domain = NULL;
- } else {
- domain = iommu_get_domain_for_dev(dev);
- }
+ handle = iommu_attach_handle_get(dev->iommu_group, pasid, 0);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
- if (!domain || !domain->iopf_handler) {
- dev_warn_ratelimited(dev,
- "iopf (pasid %d) without domain attached or handler installed\n",
- fault->prm.pasid);
+ group->attach_handle = handle;
+ group->domain = handle->domain;
- return NULL;
- }
-
- return domain;
+ return 0;
}
/* Non-last request of a group. Postpone until the last one. */
@@ -206,8 +197,11 @@ void iommu_report_device_fault(struct device *dev, struct iopf_fault *evt)
if (group == &abort_group)
goto err_abort;
- group->domain = get_domain_for_iopf(dev, fault);
- if (!group->domain)
+ if (!(fault->prm.flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID) ||
+ get_attach_handle_for_iopf(dev, fault->prm.pasid, group))
+ get_attach_handle_for_iopf(dev, IOMMU_NO_PASID, group);
+
+ if (!group->attach_handle || !group->domain->iopf_handler)
goto err_abort;
/*
--
2.34.1
> From: Lu Baolu <baolu.lu@linux.intel.com>
> Sent: Tuesday, April 30, 2024 10:57 PM
>
> Previously, the domain that a page fault targets is stored in an
> iopf_group, which represents a minimal set of page faults. With the
> introduction of attachment handle, replace the domain with the handle
It's better to use 'attach handle' as the code does.
> + handle = iommu_attach_handle_get(dev->iommu_group, pasid, 0);
> + if (IS_ERR(handle))
> + return PTR_ERR(handle);
>
> - if (!domain || !domain->iopf_handler) {
> - dev_warn_ratelimited(dev,
> - "iopf (pasid %d) without domain attached or handler
> installed\n",
> - fault->prm.pasid);
> + group->attach_handle = handle;
> + group->domain = handle->domain;
this change also removes the warning message. Is it desired?
On 2024/5/15 15:31, Tian, Kevin wrote:
>> From: Lu Baolu <baolu.lu@linux.intel.com>
>> Sent: Tuesday, April 30, 2024 10:57 PM
>>
>> Previously, the domain that a page fault targets is stored in an
>> iopf_group, which represents a minimal set of page faults. With the
>> introduction of attachment handle, replace the domain with the handle
>
> It's better to use 'attach handle' as the code does.
Done.
>
>> + handle = iommu_attach_handle_get(dev->iommu_group, pasid, 0);
>> + if (IS_ERR(handle))
>> + return PTR_ERR(handle);
>>
>> - if (!domain || !domain->iopf_handler) {
>> - dev_warn_ratelimited(dev,
>> - "iopf (pasid %d) without domain attached or handler
>> installed\n",
>> - fault->prm.pasid);
>> + group->attach_handle = handle;
>> + group->domain = handle->domain;
>
> this change also removes the warning message. Is it desired?
Not desired.
Perhaps we can add a message when the iopf handling is aborted.
Something like below:
err_abort:
+ dev_warn_ratelimited(dev, "iopf with pasid %d aborted\n",
+ fault->prm.pasid);
iopf_group_response(group, IOMMU_PAGE_RESP_FAILURE);
Though I am not sure which is better dev_warn() or dev_info().
Best regards,
baolu
> From: Baolu Lu <baolu.lu@linux.intel.com>
> Sent: Sunday, May 19, 2024 10:04 PM
>
> On 2024/5/15 15:31, Tian, Kevin wrote:
> >> From: Lu Baolu <baolu.lu@linux.intel.com>
> >> Sent: Tuesday, April 30, 2024 10:57 PM
> >>
> >> + handle = iommu_attach_handle_get(dev->iommu_group, pasid, 0);
> >> + if (IS_ERR(handle))
> >> + return PTR_ERR(handle);
> >>
> >> - if (!domain || !domain->iopf_handler) {
> >> - dev_warn_ratelimited(dev,
> >> - "iopf (pasid %d) without domain attached or handler
> >> installed\n",
> >> - fault->prm.pasid);
> >> + group->attach_handle = handle;
> >> + group->domain = handle->domain;
> >
> > this change also removes the warning message. Is it desired?
>
> Not desired.
>
> Perhaps we can add a message when the iopf handling is aborted.
> Something like below:
>
> err_abort:
> + dev_warn_ratelimited(dev, "iopf with pasid %d aborted\n",
> + fault->prm.pasid);
> iopf_group_response(group, IOMMU_PAGE_RESP_FAILURE);
>
> Though I am not sure which is better dev_warn() or dev_info().
>
yes this works. dev_warn() is fine as long as we don't expect it to
happen in sane cases.
On Tue, Apr 30, 2024 at 10:57:04PM +0800, Lu Baolu wrote: > @@ -206,8 +197,11 @@ void iommu_report_device_fault(struct device *dev, struct iopf_fault *evt) > if (group == &abort_group) > goto err_abort; > > - group->domain = get_domain_for_iopf(dev, fault); > - if (!group->domain) > + if (!(fault->prm.flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID) || > + get_attach_handle_for_iopf(dev, fault->prm.pasid, group)) > + get_attach_handle_for_iopf(dev, IOMMU_NO_PASID, group); That seems a bit weird looking? get_attach_handle_for_iopf(dev, (fault->prm.flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID) ? fault->prm.pasid : IOMMU_NO_PASID, group); Jason
On 5/8/24 8:04 AM, Jason Gunthorpe wrote: > On Tue, Apr 30, 2024 at 10:57:04PM +0800, Lu Baolu wrote: >> @@ -206,8 +197,11 @@ void iommu_report_device_fault(struct device *dev, struct iopf_fault *evt) >> if (group == &abort_group) >> goto err_abort; >> >> - group->domain = get_domain_for_iopf(dev, fault); >> - if (!group->domain) >> + if (!(fault->prm.flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID) || >> + get_attach_handle_for_iopf(dev, fault->prm.pasid, group)) >> + get_attach_handle_for_iopf(dev, IOMMU_NO_PASID, group); > That seems a bit weird looking? Agreed. > get_attach_handle_for_iopf(dev, > (fault->prm.flags & > IOMMU_FAULT_PAGE_REQUEST_PASID_VALID) ? fault->prm.pasid : IOMMU_NO_PASID, > group); The logic here is that it tries the PASID domain and if it doesn't exist, then tries the RID domain as well. I explained this in the commit message: " ... if the pasid table of a device is wholly managed by user space, there is no domain attached to the PASID of the device ... " Perhaps I can improve it like this, int rc = -EINVAL; ... if (fault->prm.flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID) rc = get_attach_handle_for_iopf(dev, fault->prm.pasid, group); if (rc) rc = get_attach_handle_for_iopf(dev, IOMMU_NO_PASID, group); if (rc || !group->attach_handle->domain->iopf_handler) goto err_abort; Best regards, baolu
On Fri, May 10, 2024 at 11:14:20AM +0800, Baolu Lu wrote: > On 5/8/24 8:04 AM, Jason Gunthorpe wrote: > > On Tue, Apr 30, 2024 at 10:57:04PM +0800, Lu Baolu wrote: > > > @@ -206,8 +197,11 @@ void iommu_report_device_fault(struct device *dev, struct iopf_fault *evt) > > > if (group == &abort_group) > > > goto err_abort; > > > - group->domain = get_domain_for_iopf(dev, fault); > > > - if (!group->domain) > > > + if (!(fault->prm.flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID) || > > > + get_attach_handle_for_iopf(dev, fault->prm.pasid, group)) > > > + get_attach_handle_for_iopf(dev, IOMMU_NO_PASID, group); > > That seems a bit weird looking? > > Agreed. > > > get_attach_handle_for_iopf(dev, > > (fault->prm.flags & > > IOMMU_FAULT_PAGE_REQUEST_PASID_VALID) ? fault->prm.pasid : IOMMU_NO_PASID, > > group); > > The logic here is that it tries the PASID domain and if it doesn't > exist, then tries the RID domain as well. I explained this in the commit > message: > > " > ... if the pasid table of a device is wholly managed by user space, > there is no domain attached to the PASID of the device ... > " Okay, it needs a comment in the code, and the RID fallback should be based aroudn checking for a NESTING domain type which includes the PASID table. (ie ARM and AMD not Intel) We shouldn't just elevate a random PASID to the RID if it isn't approprite.. Jason
On 2024/5/10 21:38, Jason Gunthorpe wrote:
> On Fri, May 10, 2024 at 11:14:20AM +0800, Baolu Lu wrote:
>> On 5/8/24 8:04 AM, Jason Gunthorpe wrote:
>>> On Tue, Apr 30, 2024 at 10:57:04PM +0800, Lu Baolu wrote:
>>>> @@ -206,8 +197,11 @@ void iommu_report_device_fault(struct device *dev, struct iopf_fault *evt)
>>>> if (group == &abort_group)
>>>> goto err_abort;
>>>> - group->domain = get_domain_for_iopf(dev, fault);
>>>> - if (!group->domain)
>>>> + if (!(fault->prm.flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID) ||
>>>> + get_attach_handle_for_iopf(dev, fault->prm.pasid, group))
>>>> + get_attach_handle_for_iopf(dev, IOMMU_NO_PASID, group);
>>> That seems a bit weird looking?
>> Agreed.
>>
>>> get_attach_handle_for_iopf(dev,
>>> (fault->prm.flags &
>>> IOMMU_FAULT_PAGE_REQUEST_PASID_VALID) ? fault->prm.pasid : IOMMU_NO_PASID,
>>> group);
>> The logic here is that it tries the PASID domain and if it doesn't
>> exist, then tries the RID domain as well. I explained this in the commit
>> message:
>>
>> "
>> ... if the pasid table of a device is wholly managed by user space,
>> there is no domain attached to the PASID of the device ...
>> "
> Okay, it needs a comment in the code, and the RID fallback should be
> based aroudn checking for a NESTING domain type which includes the
> PASID table. (ie ARM and AMD not Intel)
It appears that Intel is just special. ARM, AMD, and RISC-V all support
a NESTING domain which includes the PASID table. So, how about defining
a special NESTING domain type for Intel? Like this,
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 35ae9a6f73d3..09b4e671dcee 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -173,6 +173,8 @@ struct iommu_domain_geometry {
#define __IOMMU_DOMAIN_NESTED (1U << 6) /* User-managed address
space nested
on a stage-2 translation
*/
+#define __IOMMU_DOMAIN_PASID (1U << 7) /* User-managed domain for a
+ specific PASID*/
#define IOMMU_DOMAIN_ALLOC_FLAGS ~__IOMMU_DOMAIN_DMA_FQ
/*
@@ -204,6 +206,9 @@ struct iommu_domain_geometry {
#define IOMMU_DOMAIN_SVA (__IOMMU_DOMAIN_SVA)
#define IOMMU_DOMAIN_PLATFORM (__IOMMU_DOMAIN_PLATFORM)
#define IOMMU_DOMAIN_NESTED (__IOMMU_DOMAIN_NESTED)
+#define IOMMU_DOMAIN_NESTED_PASID \
+ (__IOMMU_DOMAIN_NESTED | \
+ __IOMMU_DOMAIN_PASID)
And current IOMMU_DOMAIN_NESTED domain type is just for a nesting domain
with PASID table.
The Intel IOMMU driver will convert to use IOMMU_DOMAIN_NESTED_PASID in
the PASID interface series.
> We shouldn't just elevate a random PASID to the RID if it isn't
> approprite..
If above propose looks good to you, then I just need to add a domain
type check before RID fallback.
Best regards,
baolu
On Fri, May 10, 2024 at 10:30:10PM +0800, Baolu Lu wrote:
> diff --git a/include/linux/iommu.h b/include/linux/iommu.h
> index 35ae9a6f73d3..09b4e671dcee 100644
> --- a/include/linux/iommu.h
> +++ b/include/linux/iommu.h
> @@ -173,6 +173,8 @@ struct iommu_domain_geometry {
>
> #define __IOMMU_DOMAIN_NESTED (1U << 6) /* User-managed address space
> nested
> on a stage-2 translation
> */
> +#define __IOMMU_DOMAIN_PASID (1U << 7) /* User-managed domain for a
> + specific PASID*/
>
> #define IOMMU_DOMAIN_ALLOC_FLAGS ~__IOMMU_DOMAIN_DMA_FQ
> /*
> @@ -204,6 +206,9 @@ struct iommu_domain_geometry {
> #define IOMMU_DOMAIN_SVA (__IOMMU_DOMAIN_SVA)
> #define IOMMU_DOMAIN_PLATFORM (__IOMMU_DOMAIN_PLATFORM)
> #define IOMMU_DOMAIN_NESTED (__IOMMU_DOMAIN_NESTED)
> +#define IOMMU_DOMAIN_NESTED_PASID \
> + (__IOMMU_DOMAIN_NESTED | \
> + __IOMMU_DOMAIN_PASID)
Yeah, something like that, I don't know about naming though..
How about
DOMAIN_NESTED = Attachment to RID or PASID
DOMAIN_NESTED_PASID_TABLE = RID and the entire PASID space
?
Jason
> From: Jason Gunthorpe <jgg@ziepe.ca>
> Sent: Saturday, May 11, 2024 12:29 AM
>
> On Fri, May 10, 2024 at 10:30:10PM +0800, Baolu Lu wrote:
>
> > diff --git a/include/linux/iommu.h b/include/linux/iommu.h
> > index 35ae9a6f73d3..09b4e671dcee 100644
> > --- a/include/linux/iommu.h
> > +++ b/include/linux/iommu.h
> > @@ -173,6 +173,8 @@ struct iommu_domain_geometry {
> >
> > #define __IOMMU_DOMAIN_NESTED (1U << 6) /* User-managed address
> space
> > nested
> > on a stage-2 translation
> > */
> > +#define __IOMMU_DOMAIN_PASID (1U << 7) /* User-managed domain
> for a
> > + specific PASID*/
> >
> > #define IOMMU_DOMAIN_ALLOC_FLAGS ~__IOMMU_DOMAIN_DMA_FQ
> > /*
> > @@ -204,6 +206,9 @@ struct iommu_domain_geometry {
> > #define IOMMU_DOMAIN_SVA (__IOMMU_DOMAIN_SVA)
> > #define IOMMU_DOMAIN_PLATFORM (__IOMMU_DOMAIN_PLATFORM)
> > #define IOMMU_DOMAIN_NESTED (__IOMMU_DOMAIN_NESTED)
> > +#define IOMMU_DOMAIN_NESTED_PASID \
> > + (__IOMMU_DOMAIN_NESTED | \
> > + __IOMMU_DOMAIN_PASID)
>
> Yeah, something like that, I don't know about naming though..
>
> How about
>
> DOMAIN_NESTED = Attachment to RID or PASID
> DOMAIN_NESTED_PASID_TABLE = RID and the entire PASID space
>
this sounds clearer
© 2016 - 2025 Red Hat, Inc.