[PATCH 08/12] iommufd/selftest: Put iopf enablement in domain attach path

Lu Baolu posted 12 patches 10 months, 1 week ago
There is a newer version of this series
[PATCH 08/12] iommufd/selftest: Put iopf enablement in domain attach path
Posted by Lu Baolu 10 months, 1 week ago
Update iopf enablement in the iommufd mock device driver to use the new
method, similar to the arm-smmu-v3 driver. Enable iopf support when any
domain with an iopf_handler is attached, and disable it when the domain
is removed.

Add a refcount in the mock device state structure to keep track of the
number of domains set to the device and PASIDs that require iopf.

Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
 drivers/iommu/iommufd/selftest.c | 52 +++++++++++++++++++++++++-------
 1 file changed, 41 insertions(+), 11 deletions(-)

diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c
index d40deb0a4f06..a6b12cee7b00 100644
--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -58,6 +58,9 @@ enum {
 	MOCK_PFN_HUGE_IOVA = _MOCK_PFN_START << 2,
 };
 
+static int mock_dev_enable_iopf(struct device *dev, struct iommu_domain *domain);
+static void mock_dev_disable_iopf(struct device *dev, struct iommu_domain *domain);
+
 /*
  * Syzkaller has trouble randomizing the correct iova to use since it is linked
  * to the map ioctl's output, and it has no ide about that. So, simplify things.
@@ -164,6 +167,7 @@ struct mock_dev {
 	unsigned long flags;
 	int id;
 	u32 cache[MOCK_DEV_CACHE_NUM];
+	unsigned int iopf_refcount;
 };
 
 static inline struct mock_dev *to_mock_dev(struct device *dev)
@@ -197,11 +201,19 @@ static int mock_domain_nop_attach(struct iommu_domain *domain,
 	if (domain->dirty_ops && (mdev->flags & MOCK_FLAGS_DEVICE_NO_DIRTY))
 		return -EINVAL;
 
+	return mock_dev_enable_iopf(dev, domain);
+}
+
+static int mock_domain_blocking_attach(struct iommu_domain *domain,
+				       struct device *dev)
+{
+	mock_dev_disable_iopf(dev, domain);
+
 	return 0;
 }
 
 static const struct iommu_domain_ops mock_blocking_ops = {
-	.attach_dev = mock_domain_nop_attach,
+	.attach_dev = mock_domain_blocking_attach,
 };
 
 static struct iommu_domain mock_blocking_domain = {
@@ -549,22 +561,42 @@ static void mock_domain_page_response(struct device *dev, struct iopf_fault *evt
 {
 }
 
-static int mock_dev_enable_feat(struct device *dev, enum iommu_dev_features feat)
+static int mock_dev_enable_iopf(struct device *dev, struct iommu_domain *domain)
 {
-	if (feat != IOMMU_DEV_FEAT_IOPF || !mock_iommu_iopf_queue)
+	struct mock_dev *mdev = to_mock_dev(dev);
+	int ret;
+
+	if (!domain->iopf_handler)
+		return 0;
+
+	if (!mock_iommu_iopf_queue)
 		return -ENODEV;
 
-	return iopf_queue_add_device(mock_iommu_iopf_queue, dev);
+	if (mdev->iopf_refcount) {
+		mdev->iopf_refcount++;
+		return 0;
+	}
+
+	ret = iopf_queue_add_device(mock_iommu_iopf_queue, dev);
+	if (ret)
+		return ret;
+
+	mdev->iopf_refcount = 1;
+
+	return 0;
 }
 
-static int mock_dev_disable_feat(struct device *dev, enum iommu_dev_features feat)
+static void mock_dev_disable_iopf(struct device *dev, struct iommu_domain *domain)
 {
-	if (feat != IOMMU_DEV_FEAT_IOPF || !mock_iommu_iopf_queue)
-		return -ENODEV;
+	struct mock_dev *mdev = to_mock_dev(dev);
+
+	if (!domain->iopf_handler)
+		return;
+
+	if (--mdev->iopf_refcount)
+		return;
 
 	iopf_queue_remove_device(mock_iommu_iopf_queue, dev);
-
-	return 0;
 }
 
 static void mock_viommu_destroy(struct iommufd_viommu *viommu)
@@ -709,8 +741,6 @@ static const struct iommu_ops mock_ops = {
 	.device_group = generic_device_group,
 	.probe_device = mock_probe_device,
 	.page_response = mock_domain_page_response,
-	.dev_enable_feat = mock_dev_enable_feat,
-	.dev_disable_feat = mock_dev_disable_feat,
 	.user_pasid_table = true,
 	.viommu_alloc = mock_viommu_alloc,
 	.default_domain_ops =
-- 
2.43.0
Re: [PATCH 08/12] iommufd/selftest: Put iopf enablement in domain attach path
Posted by Jason Gunthorpe 10 months ago
On Fri, Feb 14, 2025 at 02:11:00PM +0800, Lu Baolu wrote:
> @@ -197,11 +201,19 @@ static int mock_domain_nop_attach(struct iommu_domain *domain,
>  	if (domain->dirty_ops && (mdev->flags & MOCK_FLAGS_DEVICE_NO_DIRTY))
>  		return -EINVAL;
>  
> +	return mock_dev_enable_iopf(dev, domain);
> +}

This isn't going to work for a replace type operation? Maybe like:

if (old_domain->iopf_handler && !domain->iopf_handler)
	return mock_dev_disable_iopf(dev, domain);
if (old_domain->iopf_handler && domain->iopf_handler)
        return 0;
return mock_dev_enable_iopf(dev, domain);

?

Jason
Re: [PATCH 08/12] iommufd/selftest: Put iopf enablement in domain attach path
Posted by Baolu Lu 10 months ago
On 2/20/25 09:02, Jason Gunthorpe wrote:
> On Fri, Feb 14, 2025 at 02:11:00PM +0800, Lu Baolu wrote:
>> @@ -197,11 +201,19 @@ static int mock_domain_nop_attach(struct iommu_domain *domain,
>>   	if (domain->dirty_ops && (mdev->flags & MOCK_FLAGS_DEVICE_NO_DIRTY))
>>   		return -EINVAL;
>>   
>> +	return mock_dev_enable_iopf(dev, domain);
>> +}
> 
> This isn't going to work for a replace type operation? Maybe like:
> 
> if (old_domain->iopf_handler && !domain->iopf_handler)
> 	return mock_dev_disable_iopf(dev, domain);
> if (old_domain->iopf_handler && domain->iopf_handler)
>          return 0;
> return mock_dev_enable_iopf(dev, domain);
> 
> ?

The iommufd mock device driver appears not to support replacement. The
replacement operation on this driver is likely handled as follows:

- attach domain_a
- attach blocking_domain
- attach domain_b

The mock_dev_disable_iopf() is called in attach_dev of the blocking
domain.

There seems to be a bug in this patch. The existing domain should be
passed to mock_dev_disable_iopf().  It requires something similar to the
following:

diff --git a/drivers/iommu/iommufd/selftest.c 
b/drivers/iommu/iommufd/selftest.c
index a6b12cee7b00..54a6f0851758 100644
--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -168,6 +168,7 @@ struct mock_dev {
         int id;
         u32 cache[MOCK_DEV_CACHE_NUM];
         unsigned int iopf_refcount;
+       struct iommu_domain *domain;
  };

  static inline struct mock_dev *to_mock_dev(struct device *dev)
@@ -197,17 +198,24 @@ static int mock_domain_nop_attach(struct 
iommu_domain *domain,
                                   struct device *dev)
  {
         struct mock_dev *mdev = to_mock_dev(dev);
+       int ret;

         if (domain->dirty_ops && (mdev->flags & 
MOCK_FLAGS_DEVICE_NO_DIRTY))
                 return -EINVAL;

-       return mock_dev_enable_iopf(dev, domain);
+       ret = mock_dev_enable_iopf(dev, domain);
+       if (ret)
+               return ret;
+
+       mdev->domain = domain;
  }

  static int mock_domain_blocking_attach(struct iommu_domain *domain,
                                        struct device *dev)
  {
-       mock_dev_disable_iopf(dev, domain);
+       struct mock_dev *mdev = to_mock_dev(dev);
+
+       mock_dev_disable_iopf(dev, mdev->domain);

         return 0;
  }

Thanks,
baolu
Re: [PATCH 08/12] iommufd/selftest: Put iopf enablement in domain attach path
Posted by Jason Gunthorpe 10 months ago
On Thu, Feb 20, 2025 at 03:03:21PM +0800, Baolu Lu wrote:
> On 2/20/25 09:02, Jason Gunthorpe wrote:
> > On Fri, Feb 14, 2025 at 02:11:00PM +0800, Lu Baolu wrote:
> > > @@ -197,11 +201,19 @@ static int mock_domain_nop_attach(struct iommu_domain *domain,
> > >   	if (domain->dirty_ops && (mdev->flags & MOCK_FLAGS_DEVICE_NO_DIRTY))
> > >   		return -EINVAL;
> > > +	return mock_dev_enable_iopf(dev, domain);
> > > +}
> > 
> > This isn't going to work for a replace type operation? Maybe like:
> > 
> > if (old_domain->iopf_handler && !domain->iopf_handler)
> > 	return mock_dev_disable_iopf(dev, domain);
> > if (old_domain->iopf_handler && domain->iopf_handler)
> >          return 0;
> > return mock_dev_enable_iopf(dev, domain);
> > 
> > ?
> 
> The iommufd mock device driver appears not to support replacement.

That's not technically a choice the driver gets to have ..

> The
> replacement operation on this driver is likely handled as follows:
> 
> - attach domain_a attach blocking_domain attach domain_b

Nothing actually does that though?

Jason
Re: [PATCH 08/12] iommufd/selftest: Put iopf enablement in domain attach path
Posted by Baolu Lu 10 months ago
On 2/21/25 02:00, Jason Gunthorpe wrote:
> On Thu, Feb 20, 2025 at 03:03:21PM +0800, Baolu Lu wrote:
>> On 2/20/25 09:02, Jason Gunthorpe wrote:
>>> On Fri, Feb 14, 2025 at 02:11:00PM +0800, Lu Baolu wrote:
>>>> @@ -197,11 +201,19 @@ static int mock_domain_nop_attach(struct iommu_domain *domain,
>>>>    	if (domain->dirty_ops && (mdev->flags & MOCK_FLAGS_DEVICE_NO_DIRTY))
>>>>    		return -EINVAL;
>>>> +	return mock_dev_enable_iopf(dev, domain);
>>>> +}
>>> This isn't going to work for a replace type operation? Maybe like:
>>>
>>> if (old_domain->iopf_handler && !domain->iopf_handler)
>>> 	return mock_dev_disable_iopf(dev, domain);
>>> if (old_domain->iopf_handler && domain->iopf_handler)
>>>           return 0;
>>> return mock_dev_enable_iopf(dev, domain);
>>>
>>> ?
>> The iommufd mock device driver appears not to support replacement.
> That's not technically a choice the driver gets to have ..

Yes.

> 
>> The
>> replacement operation on this driver is likely handled as follows:
>>
>> - attach domain_a attach blocking_domain attach domain_b
> Nothing actually does that though?

Ah! you are right.

This driver allows a new domain to be set even if a domain already
exists. This is different from what vt-d driver does, which removes the
old domain first. So perhaps we need to enhance it in two ways:

- Store the existing domain (a.k.a. old domain).
- Handle iopf enablement, taking the existing domain into account.

How about below change?

diff --git a/drivers/iommu/iommufd/selftest.c 
b/drivers/iommu/iommufd/selftest.c
index a6b12cee7b00..5ffbd4e3f372 100644
--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -168,6 +168,7 @@ struct mock_dev {
         int id;
         u32 cache[MOCK_DEV_CACHE_NUM];
         unsigned int iopf_refcount;
+       struct iommu_domain *domain;
  };

  static inline struct mock_dev *to_mock_dev(struct device *dev)
@@ -197,17 +198,28 @@ static int mock_domain_nop_attach(struct 
iommu_domain *domain,
                                   struct device *dev)
  {
         struct mock_dev *mdev = to_mock_dev(dev);
+       int ret;

         if (domain->dirty_ops && (mdev->flags & 
MOCK_FLAGS_DEVICE_NO_DIRTY))
                 return -EINVAL;

-       return mock_dev_enable_iopf(dev, domain);
+       if (mdev->domain)
+               mock_dev_disable_iopf(dev, mdev->domain);
+
+       ret = mock_dev_enable_iopf(dev, domain);
+       if (ret)
+               return ret;
+
+       mdev->domain = domain;
+       return 0;
  }

Thanks,
baolu
Re: [PATCH 08/12] iommufd/selftest: Put iopf enablement in domain attach path
Posted by Jason Gunthorpe 10 months ago
On Fri, Feb 21, 2025 at 09:31:48AM +0800, Baolu Lu wrote:

> How about below change?

Sure

> -       return mock_dev_enable_iopf(dev, domain);
> +       if (mdev->domain)
> +               mock_dev_disable_iopf(dev, mdev->domain);
> +
> +       ret = mock_dev_enable_iopf(dev, domain);
> +       if (ret)

Though here the domain is disabled but not removed from mdev->domain,
is it OK?

Jason
Re: [PATCH 08/12] iommufd/selftest: Put iopf enablement in domain attach path
Posted by Baolu Lu 10 months ago
On 2/21/25 23:04, Jason Gunthorpe wrote:
>> -       return mock_dev_enable_iopf(dev, domain);
>> +       if (mdev->domain)
>> +               mock_dev_disable_iopf(dev, mdev->domain);
>> +
>> +       ret = mock_dev_enable_iopf(dev, domain);
>> +       if (ret)
> Though here the domain is disabled but not removed from mdev->domain,
> is it OK?

That's not okay. I can make it like below:

static int mock_domain_nop_attach(struct iommu_domain *domain,
                                   struct device *dev)
{
         struct mock_dev *mdev = to_mock_dev(dev);
         int ret;

         if (domain->dirty_ops && (mdev->flags & 
MOCK_FLAGS_DEVICE_NO_DIRTY))
                 return -EINVAL;

         ret = mock_dev_enable_iopf(dev, domain);
         if (ret)
                 return ret;

         mock_dev_disable_iopf(dev, mdev->domain);
         mdev->domain = domain;

         return 0;
}

Both mock_dev_enable/disable_iopf() will be a no-op if domain or
domain's iopf handler is empty:

         if (!domain || !domain->iopf_handler)
                 return;

Does it work for you?

Thanks,
baolu
Re: [PATCH 08/12] iommufd/selftest: Put iopf enablement in domain attach path
Posted by Jason Gunthorpe 9 months, 3 weeks ago
On Sat, Feb 22, 2025 at 03:25:43PM +0800, Baolu Lu wrote:
> On 2/21/25 23:04, Jason Gunthorpe wrote:
> > > -       return mock_dev_enable_iopf(dev, domain);
> > > +       if (mdev->domain)
> > > +               mock_dev_disable_iopf(dev, mdev->domain);
> > > +
> > > +       ret = mock_dev_enable_iopf(dev, domain);
> > > +       if (ret)
> > Though here the domain is disabled but not removed from mdev->domain,
> > is it OK?
> 
> That's not okay. I can make it like below:
> 
> static int mock_domain_nop_attach(struct iommu_domain *domain,
>                                   struct device *dev)
> {
>         struct mock_dev *mdev = to_mock_dev(dev);
>         int ret;
> 
>         if (domain->dirty_ops && (mdev->flags & MOCK_FLAGS_DEVICE_NO_DIRTY))
>                 return -EINVAL;
> 
>         ret = mock_dev_enable_iopf(dev, domain);
>         if (ret)
>                 return ret;
> 
>         mock_dev_disable_iopf(dev, mdev->domain);
>         mdev->domain = domain;
> 
>         return 0;
> }
> 
> Both mock_dev_enable/disable_iopf() will be a no-op if domain or
> domain's iopf handler is empty:
> 
>         if (!domain || !domain->iopf_handler)
>                 return;
> 
> Does it work for you?

Yeah that looks Ok

Jason