diff mbox series

[2/5] iommu: Ensure device has the same iommu_ops as the domain

Message ID 20220606061927.26049-3-nicolinc@nvidia.com (mailing list archive)
State Superseded
Headers show
Series Simplify vfio_iommu_type1 attach/detach routine | expand

Commit Message

Nicolin Chen June 6, 2022, 6:19 a.m. UTC
The core code should not call an iommu driver op with a struct device
parameter unless it knows that the dev_iommu_priv_get() for that struct
device was setup by the same driver. Otherwise in a mixed driver system
the iommu_priv could be casted to the wrong type.

Store the iommu_ops pointer in the iommu_domain and use it as a check to
validate that the struct device is correct before invoking any domain op
that accepts a struct device.

This allows removing the check of the domain op equality in VFIO.

Co-developed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
 drivers/iommu/amd/iommu.c                   | 1 +
 drivers/iommu/apple-dart.c                  | 1 +
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 1 +
 drivers/iommu/arm/arm-smmu/arm-smmu.c       | 1 +
 drivers/iommu/arm/arm-smmu/qcom_iommu.c     | 1 +
 drivers/iommu/exynos-iommu.c                | 1 +
 drivers/iommu/fsl_pamu_domain.c             | 1 +
 drivers/iommu/intel/iommu.c                 | 1 +
 drivers/iommu/iommu.c                       | 4 ++++
 drivers/iommu/ipmmu-vmsa.c                  | 1 +
 drivers/iommu/msm_iommu.c                   | 1 +
 drivers/iommu/mtk_iommu.c                   | 1 +
 drivers/iommu/mtk_iommu_v1.c                | 1 +
 drivers/iommu/omap-iommu.c                  | 1 +
 drivers/iommu/rockchip-iommu.c              | 1 +
 drivers/iommu/s390-iommu.c                  | 1 +
 drivers/iommu/sprd-iommu.c                  | 1 +
 drivers/iommu/sun50i-iommu.c                | 1 +
 drivers/iommu/tegra-gart.c                  | 1 +
 drivers/iommu/tegra-smmu.c                  | 1 +
 drivers/iommu/virtio-iommu.c                | 1 +
 include/linux/iommu.h                       | 2 ++
 22 files changed, 26 insertions(+)

Comments

Robin Murphy June 6, 2022, 2:33 p.m. UTC | #1
On 2022-06-06 07:19, Nicolin Chen wrote:
> The core code should not call an iommu driver op with a struct device
> parameter unless it knows that the dev_iommu_priv_get() for that struct
> device was setup by the same driver. Otherwise in a mixed driver system
> the iommu_priv could be casted to the wrong type.

We don't have mixed-driver systems, and there are plenty more 
significant problems than this one to solve before we can (but thanks 
for pointing it out - I hadn't got as far as auditing the public 
interfaces yet). Once domains are allocated via a particular device's 
IOMMU instance in the first place, there will be ample opportunity for 
the core to stash suitable identifying information in the domain for 
itself. TBH even the current code could do it without needing the 
weirdly invasive changes here.

> Store the iommu_ops pointer in the iommu_domain and use it as a check to
> validate that the struct device is correct before invoking any domain op
> that accepts a struct device.

In fact this even describes exactly that - "Store the iommu_ops pointer 
in the iommu_domain", vs. the "Store the iommu_ops pointer in the 
iommu_domain_ops" which the patch is actually doing :/

[...]
> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> index 19cf28d40ebe..8a1f437a51f2 100644
> --- a/drivers/iommu/iommu.c
> +++ b/drivers/iommu/iommu.c
> @@ -1963,6 +1963,10 @@ static int __iommu_attach_device(struct iommu_domain *domain,
>   {
>   	int ret;
>   
> +	/* Ensure the device was probe'd onto the same driver as the domain */
> +	if (dev->bus->iommu_ops != domain->ops->iommu_ops)

Nope, dev_iommu_ops(dev) please. Furthermore I think the logical place 
to put this is in iommu_group_do_attach_device(), since that's the 
gateway for the public interfaces - we shouldn't need to second-guess 
ourselves for internal default-domain-related calls.

Thanks,
Robin.

> +		return -EMEDIUMTYPE;
> +
>   	if (unlikely(domain->ops->attach_dev == NULL))
>   		return -ENODEV;
Nicolin Chen June 6, 2022, 4:51 p.m. UTC | #2
Hi Robin,

On Mon, Jun 06, 2022 at 03:33:42PM +0100, Robin Murphy wrote:
> On 2022-06-06 07:19, Nicolin Chen wrote:
> > The core code should not call an iommu driver op with a struct device
> > parameter unless it knows that the dev_iommu_priv_get() for that struct
> > device was setup by the same driver. Otherwise in a mixed driver system
> > the iommu_priv could be casted to the wrong type.
> 
> We don't have mixed-driver systems, and there are plenty more
> significant problems than this one to solve before we can (but thanks
> for pointing it out - I hadn't got as far as auditing the public
> interfaces yet). Once domains are allocated via a particular device's
> IOMMU instance in the first place, there will be ample opportunity for
> the core to stash suitable identifying information in the domain for
> itself. TBH even the current code could do it without needing the
> weirdly invasive changes here.

Do you have an alternative and less invasive solution in mind?

> > Store the iommu_ops pointer in the iommu_domain and use it as a check to
> > validate that the struct device is correct before invoking any domain op
> > that accepts a struct device.
> 
> In fact this even describes exactly that - "Store the iommu_ops pointer
> in the iommu_domain", vs. the "Store the iommu_ops pointer in the
> iommu_domain_ops" which the patch is actually doing :/

Will fix that.

> [...]
> > diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> > index 19cf28d40ebe..8a1f437a51f2 100644
> > --- a/drivers/iommu/iommu.c
> > +++ b/drivers/iommu/iommu.c
> > @@ -1963,6 +1963,10 @@ static int __iommu_attach_device(struct iommu_domain *domain,
> >   {
> >       int ret;
> > 
> > +     /* Ensure the device was probe'd onto the same driver as the domain */
> > +     if (dev->bus->iommu_ops != domain->ops->iommu_ops)
> 
> Nope, dev_iommu_ops(dev) please. Furthermore I think the logical place
> to put this is in iommu_group_do_attach_device(), since that's the
> gateway for the public interfaces - we shouldn't need to second-guess
> ourselves for internal default-domain-related calls.

Will move to iommu_group_do_attach_device and change to dev_iommu_ops.

Thanks!
Nic
Robin Murphy June 6, 2022, 5:50 p.m. UTC | #3
On 2022-06-06 17:51, Nicolin Chen wrote:
> Hi Robin,
> 
> On Mon, Jun 06, 2022 at 03:33:42PM +0100, Robin Murphy wrote:
>> On 2022-06-06 07:19, Nicolin Chen wrote:
>>> The core code should not call an iommu driver op with a struct device
>>> parameter unless it knows that the dev_iommu_priv_get() for that struct
>>> device was setup by the same driver. Otherwise in a mixed driver system
>>> the iommu_priv could be casted to the wrong type.
>>
>> We don't have mixed-driver systems, and there are plenty more
>> significant problems than this one to solve before we can (but thanks
>> for pointing it out - I hadn't got as far as auditing the public
>> interfaces yet). Once domains are allocated via a particular device's
>> IOMMU instance in the first place, there will be ample opportunity for
>> the core to stash suitable identifying information in the domain for
>> itself. TBH even the current code could do it without needing the
>> weirdly invasive changes here.
> 
> Do you have an alternative and less invasive solution in mind?
> 
>>> Store the iommu_ops pointer in the iommu_domain and use it as a check to
>>> validate that the struct device is correct before invoking any domain op
>>> that accepts a struct device.
>>
>> In fact this even describes exactly that - "Store the iommu_ops pointer
>> in the iommu_domain", vs. the "Store the iommu_ops pointer in the
>> iommu_domain_ops" which the patch is actually doing :/
> 
> Will fix that.

Well, as before I'd prefer to make the code match the commit message - 
if I really need to spell it out, see below - since I can't imagine that 
we should ever have need to identify a set of iommu_domain_ops in 
isolation, therefore I think it's considerably clearer to use the 
iommu_domain itself. However, either way we really don't need this yet, 
so we may as well just go ahead and remove the redundant test from VFIO 
anyway, and I can add some form of this patch to my dev branch for now.

Thanks,
Robin.

----->8-----
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index cde2e1d6ab9b..72990edc9314 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -1902,6 +1902,7 @@ static struct iommu_domain 
*__iommu_domain_alloc(struct device *dev,
  	domain->type = type;
  	/* Assume all sizes by default; the driver may override this later */
  	domain->pgsize_bitmap = ops->pgsize_bitmap;
+	domain->owner = ops;
  	if (!domain->ops)
  		domain->ops = ops->default_domain_ops;

diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 6f64cbbc6721..79e557207f53 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -89,6 +89,7 @@ struct iommu_domain_geometry {

  struct iommu_domain {
  	unsigned type;
+	const struct iommu_ops *owner; /* Who allocated this domain */
  	const struct iommu_domain_ops *ops;
  	unsigned long pgsize_bitmap;	/* Bitmap of page sizes in use */
  	iommu_fault_handler_t handler;
Nicolin Chen June 6, 2022, 6:28 p.m. UTC | #4
On Mon, Jun 06, 2022 at 06:50:33PM +0100, Robin Murphy wrote:
> External email: Use caution opening links or attachments
> 
> 
> On 2022-06-06 17:51, Nicolin Chen wrote:
> > Hi Robin,
> > 
> > On Mon, Jun 06, 2022 at 03:33:42PM +0100, Robin Murphy wrote:
> > > On 2022-06-06 07:19, Nicolin Chen wrote:
> > > > The core code should not call an iommu driver op with a struct device
> > > > parameter unless it knows that the dev_iommu_priv_get() for that struct
> > > > device was setup by the same driver. Otherwise in a mixed driver system
> > > > the iommu_priv could be casted to the wrong type.
> > > 
> > > We don't have mixed-driver systems, and there are plenty more
> > > significant problems than this one to solve before we can (but thanks
> > > for pointing it out - I hadn't got as far as auditing the public
> > > interfaces yet). Once domains are allocated via a particular device's
> > > IOMMU instance in the first place, there will be ample opportunity for
> > > the core to stash suitable identifying information in the domain for
> > > itself. TBH even the current code could do it without needing the
> > > weirdly invasive changes here.
> > 
> > Do you have an alternative and less invasive solution in mind?
> > 
> > > > Store the iommu_ops pointer in the iommu_domain and use it as a check to
> > > > validate that the struct device is correct before invoking any domain op
> > > > that accepts a struct device.
> > > 
> > > In fact this even describes exactly that - "Store the iommu_ops pointer
> > > in the iommu_domain", vs. the "Store the iommu_ops pointer in the
> > > iommu_domain_ops" which the patch is actually doing :/
> > 
> > Will fix that.
> 
> Well, as before I'd prefer to make the code match the commit message -
> if I really need to spell it out, see below - since I can't imagine that
> we should ever have need to identify a set of iommu_domain_ops in
> isolation, therefore I think it's considerably clearer to use the
> iommu_domain itself. However, either way we really don't need this yet,
> so we may as well just go ahead and remove the redundant test from VFIO
> anyway, and I can add some form of this patch to my dev branch for now.

I see. The version below is much cleaner. Yet, it'd become having a
common pointer per iommu_domain vs. one pointer per driver. Jason
pointed it out to me earlier that by doing so memory waste would be
unnecessary on platforms that have considerable numbers of masters.

Since we know that it'd be safe to exclude this single change from
this series, I can drop it in next version, if you don't like the
change.

Thanks!
Nic

> ----->8-----
> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> index cde2e1d6ab9b..72990edc9314 100644
> --- a/drivers/iommu/iommu.c
> +++ b/drivers/iommu/iommu.c
> @@ -1902,6 +1902,7 @@ static struct iommu_domain
> *__iommu_domain_alloc(struct device *dev,
>        domain->type = type;
>        /* Assume all sizes by default; the driver may override this later */
>        domain->pgsize_bitmap = ops->pgsize_bitmap;
> +       domain->owner = ops;
>        if (!domain->ops)
>                domain->ops = ops->default_domain_ops;
> 
> diff --git a/include/linux/iommu.h b/include/linux/iommu.h
> index 6f64cbbc6721..79e557207f53 100644
> --- a/include/linux/iommu.h
> +++ b/include/linux/iommu.h
> @@ -89,6 +89,7 @@ struct iommu_domain_geometry {
> 
>  struct iommu_domain {
>        unsigned type;
> +       const struct iommu_ops *owner; /* Who allocated this domain */
>        const struct iommu_domain_ops *ops;
>        unsigned long pgsize_bitmap;    /* Bitmap of page sizes in use */
>        iommu_fault_handler_t handler;
Jason Gunthorpe June 6, 2022, 6:52 p.m. UTC | #5
On Mon, Jun 06, 2022 at 11:28:49AM -0700, Nicolin Chen wrote:

> > Well, as before I'd prefer to make the code match the commit message -
> > if I really need to spell it out, see below - since I can't imagine that
> > we should ever have need to identify a set of iommu_domain_ops in
> > isolation, therefore I think it's considerably clearer to use the
> > iommu_domain itself. However, either way we really don't need this yet,
> > so we may as well just go ahead and remove the redundant test from VFIO
> > anyway, and I can add some form of this patch to my dev branch for now.
> 
> I see. The version below is much cleaner. Yet, it'd become having a
> common pointer per iommu_domain vs. one pointer per driver. Jason
> pointed it out to me earlier that by doing so memory waste would be
> unnecessary on platforms that have considerable numbers of masters.

I had ment using struct iommu_domain when there is a simple solution
to use rodata doesn't seem ideal.

I don't quite understand the reluctance to make small changes to
drivers, it was the same logic with the default_domain_ops thing too.

> Since we know that it'd be safe to exclude this single change from
> this series, I can drop it in next version, if you don't like the
> change.

But this is fine too, that really is half the point of this series..

Jason
diff mbox series

Patch

diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index ad499658a6b6..679f7a265013 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -2285,6 +2285,7 @@  const struct iommu_ops amd_iommu_ops = {
 	.pgsize_bitmap	= AMD_IOMMU_PGSIZES,
 	.def_domain_type = amd_iommu_def_domain_type,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops	= &amd_iommu_ops,
 		.attach_dev	= amd_iommu_attach_device,
 		.detach_dev	= amd_iommu_detach_device,
 		.map		= amd_iommu_map,
diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c
index e58dc310afd7..3d36d9a12aa7 100644
--- a/drivers/iommu/apple-dart.c
+++ b/drivers/iommu/apple-dart.c
@@ -775,6 +775,7 @@  static const struct iommu_ops apple_dart_iommu_ops = {
 	.pgsize_bitmap = -1UL, /* Restricted during dart probe */
 	.owner = THIS_MODULE,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops	= &apple_dart_iommu_ops,
 		.attach_dev	= apple_dart_attach_dev,
 		.detach_dev	= apple_dart_detach_dev,
 		.map_pages	= apple_dart_map_pages,
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 6c393cd84925..471ceb60427c 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2859,6 +2859,7 @@  static struct iommu_ops arm_smmu_ops = {
 	.pgsize_bitmap		= -1UL, /* Restricted during device attach */
 	.owner			= THIS_MODULE,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops		= &arm_smmu_ops,
 		.attach_dev		= arm_smmu_attach_dev,
 		.map_pages		= arm_smmu_map_pages,
 		.unmap_pages		= arm_smmu_unmap_pages,
diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c
index 2ed3594f384e..52c2589a4deb 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu.c
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c
@@ -1597,6 +1597,7 @@  static struct iommu_ops arm_smmu_ops = {
 	.pgsize_bitmap		= -1UL, /* Restricted during device attach */
 	.owner			= THIS_MODULE,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops		= &arm_smmu_ops,
 		.attach_dev		= arm_smmu_attach_dev,
 		.map_pages		= arm_smmu_map_pages,
 		.unmap_pages		= arm_smmu_unmap_pages,
diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
index a8b63b855ffb..8806a621f81e 100644
--- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c
+++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
@@ -596,6 +596,7 @@  static const struct iommu_ops qcom_iommu_ops = {
 	.of_xlate	= qcom_iommu_of_xlate,
 	.pgsize_bitmap	= SZ_4K | SZ_64K | SZ_1M | SZ_16M,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops	= &qcom_iommu_ops,
 		.attach_dev	= qcom_iommu_attach_dev,
 		.detach_dev	= qcom_iommu_detach_dev,
 		.map		= qcom_iommu_map,
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 71f2018e23fe..fa93f94313e3 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -1315,6 +1315,7 @@  static const struct iommu_ops exynos_iommu_ops = {
 	.pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE,
 	.of_xlate = exynos_iommu_of_xlate,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops	= &exynos_iommu_ops,
 		.attach_dev	= exynos_iommu_attach_device,
 		.detach_dev	= exynos_iommu_detach_device,
 		.map		= exynos_iommu_map,
diff --git a/drivers/iommu/fsl_pamu_domain.c b/drivers/iommu/fsl_pamu_domain.c
index 94b4589dc67c..7bdce4168d2c 100644
--- a/drivers/iommu/fsl_pamu_domain.c
+++ b/drivers/iommu/fsl_pamu_domain.c
@@ -458,6 +458,7 @@  static const struct iommu_ops fsl_pamu_ops = {
 	.release_device	= fsl_pamu_release_device,
 	.device_group   = fsl_pamu_device_group,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops	= &fsl_pamu_ops,
 		.attach_dev	= fsl_pamu_attach_device,
 		.detach_dev	= fsl_pamu_detach_device,
 		.iova_to_phys	= fsl_pamu_iova_to_phys,
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 0813b119d680..c6022484ca2d 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -4925,6 +4925,7 @@  const struct iommu_ops intel_iommu_ops = {
 	.page_response		= intel_svm_page_response,
 #endif
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops		= &intel_iommu_ops,
 		.attach_dev		= intel_iommu_attach_device,
 		.detach_dev		= intel_iommu_detach_device,
 		.map_pages		= intel_iommu_map_pages,
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 19cf28d40ebe..8a1f437a51f2 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -1963,6 +1963,10 @@  static int __iommu_attach_device(struct iommu_domain *domain,
 {
 	int ret;
 
+	/* Ensure the device was probe'd onto the same driver as the domain */
+	if (dev->bus->iommu_ops != domain->ops->iommu_ops)
+		return -EMEDIUMTYPE;
+
 	if (unlikely(domain->ops->attach_dev == NULL))
 		return -ENODEV;
 
diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c
index e491e410add5..767b93da5800 100644
--- a/drivers/iommu/ipmmu-vmsa.c
+++ b/drivers/iommu/ipmmu-vmsa.c
@@ -877,6 +877,7 @@  static const struct iommu_ops ipmmu_ops = {
 	.pgsize_bitmap = SZ_1G | SZ_2M | SZ_4K,
 	.of_xlate = ipmmu_of_xlate,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops	= &ipmmu_ops,
 		.attach_dev	= ipmmu_attach_device,
 		.detach_dev	= ipmmu_detach_device,
 		.map		= ipmmu_map,
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index f09aedfdd462..29f6a6d5691e 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -682,6 +682,7 @@  static struct iommu_ops msm_iommu_ops = {
 	.pgsize_bitmap = MSM_IOMMU_PGSIZES,
 	.of_xlate = qcom_iommu_of_xlate,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops	= &msm_iommu_ops,
 		.attach_dev	= msm_iommu_attach_dev,
 		.detach_dev	= msm_iommu_detach_dev,
 		.map		= msm_iommu_map,
diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
index bb9dd92c9898..c5c45f65077d 100644
--- a/drivers/iommu/mtk_iommu.c
+++ b/drivers/iommu/mtk_iommu.c
@@ -937,6 +937,7 @@  static const struct iommu_ops mtk_iommu_ops = {
 	.pgsize_bitmap	= SZ_4K | SZ_64K | SZ_1M | SZ_16M,
 	.owner		= THIS_MODULE,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops	= &mtk_iommu_ops,
 		.attach_dev	= mtk_iommu_attach_device,
 		.detach_dev	= mtk_iommu_detach_device,
 		.map		= mtk_iommu_map,
diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c
index e1cb51b9866c..77c53580f730 100644
--- a/drivers/iommu/mtk_iommu_v1.c
+++ b/drivers/iommu/mtk_iommu_v1.c
@@ -594,6 +594,7 @@  static const struct iommu_ops mtk_iommu_v1_ops = {
 	.pgsize_bitmap	= ~0UL << MT2701_IOMMU_PAGE_SHIFT,
 	.owner          = THIS_MODULE,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops	= &mtk_iommu_v1_ops,
 		.attach_dev	= mtk_iommu_v1_attach_device,
 		.detach_dev	= mtk_iommu_v1_detach_device,
 		.map		= mtk_iommu_v1_map,
diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
index bbc6c4cd7aae..a0bf85ccebcd 100644
--- a/drivers/iommu/omap-iommu.c
+++ b/drivers/iommu/omap-iommu.c
@@ -1739,6 +1739,7 @@  static const struct iommu_ops omap_iommu_ops = {
 	.device_group	= omap_iommu_device_group,
 	.pgsize_bitmap	= OMAP_IOMMU_PGSIZES,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops	= &omap_iommu_ops,
 		.attach_dev	= omap_iommu_attach_dev,
 		.detach_dev	= omap_iommu_detach_dev,
 		.map		= omap_iommu_map,
diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index ab57c4b8fade..5f5387e902e0 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -1193,6 +1193,7 @@  static const struct iommu_ops rk_iommu_ops = {
 	.pgsize_bitmap = RK_IOMMU_PGSIZE_BITMAP,
 	.of_xlate = rk_iommu_of_xlate,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops	= &rk_iommu_ops,
 		.attach_dev	= rk_iommu_attach_device,
 		.detach_dev	= rk_iommu_detach_device,
 		.map		= rk_iommu_map,
diff --git a/drivers/iommu/s390-iommu.c b/drivers/iommu/s390-iommu.c
index c898bcbbce11..62e6d152b0a0 100644
--- a/drivers/iommu/s390-iommu.c
+++ b/drivers/iommu/s390-iommu.c
@@ -377,6 +377,7 @@  static const struct iommu_ops s390_iommu_ops = {
 	.device_group = generic_device_group,
 	.pgsize_bitmap = S390_IOMMU_PGSIZES,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops	= &s390_iommu_ops,
 		.attach_dev	= s390_iommu_attach_device,
 		.detach_dev	= s390_iommu_detach_device,
 		.map		= s390_iommu_map,
diff --git a/drivers/iommu/sprd-iommu.c b/drivers/iommu/sprd-iommu.c
index bd409bab6286..6e8ca34d6a00 100644
--- a/drivers/iommu/sprd-iommu.c
+++ b/drivers/iommu/sprd-iommu.c
@@ -423,6 +423,7 @@  static const struct iommu_ops sprd_iommu_ops = {
 	.pgsize_bitmap	= ~0UL << SPRD_IOMMU_PAGE_SHIFT,
 	.owner		= THIS_MODULE,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops	= &sprd_iommu_ops,
 		.attach_dev	= sprd_iommu_attach_device,
 		.detach_dev	= sprd_iommu_detach_device,
 		.map		= sprd_iommu_map,
diff --git a/drivers/iommu/sun50i-iommu.c b/drivers/iommu/sun50i-iommu.c
index c54ab477b8fd..560cff8e0f04 100644
--- a/drivers/iommu/sun50i-iommu.c
+++ b/drivers/iommu/sun50i-iommu.c
@@ -766,6 +766,7 @@  static const struct iommu_ops sun50i_iommu_ops = {
 	.probe_device	= sun50i_iommu_probe_device,
 	.release_device	= sun50i_iommu_release_device,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops	= &sun50i_iommu_ops,
 		.attach_dev	= sun50i_iommu_attach_device,
 		.detach_dev	= sun50i_iommu_detach_device,
 		.flush_iotlb_all = sun50i_iommu_flush_iotlb_all,
diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c
index a6700a40a6f8..cd4553611cc9 100644
--- a/drivers/iommu/tegra-gart.c
+++ b/drivers/iommu/tegra-gart.c
@@ -278,6 +278,7 @@  static const struct iommu_ops gart_iommu_ops = {
 	.pgsize_bitmap	= GART_IOMMU_PGSIZES,
 	.of_xlate	= gart_iommu_of_xlate,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops	= &gart_iommu_ops,
 		.attach_dev	= gart_iommu_attach_dev,
 		.detach_dev	= gart_iommu_detach_dev,
 		.map		= gart_iommu_map,
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 2f2b12033618..67c101d1ad66 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -971,6 +971,7 @@  static const struct iommu_ops tegra_smmu_ops = {
 	.of_xlate = tegra_smmu_of_xlate,
 	.pgsize_bitmap = SZ_4K,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops	= &tegra_smmu_ops,
 		.attach_dev	= tegra_smmu_attach_dev,
 		.detach_dev	= tegra_smmu_detach_dev,
 		.map		= tegra_smmu_map,
diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c
index e3b812d8fa96..703d87922786 100644
--- a/drivers/iommu/virtio-iommu.c
+++ b/drivers/iommu/virtio-iommu.c
@@ -1017,6 +1017,7 @@  static struct iommu_ops viommu_ops = {
 	.of_xlate		= viommu_of_xlate,
 	.owner			= THIS_MODULE,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
+		.iommu_ops		= &viommu_ops,
 		.attach_dev		= viommu_attach_dev,
 		.map			= viommu_map,
 		.unmap			= viommu_unmap,
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 5e1afe169549..77deaf4fc7f8 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -261,6 +261,7 @@  struct iommu_ops {
 
 /**
  * struct iommu_domain_ops - domain specific operations
+ * @iommu_ops: Pointer to the ops associated with compatible devices
  * @attach_dev: attach an iommu domain to a device
  * @detach_dev: detach an iommu domain from a device
  * @map: map a physically contiguous memory region to an iommu domain
@@ -281,6 +282,7 @@  struct iommu_ops {
  * @free: Release the domain after use.
  */
 struct iommu_domain_ops {
+	const struct iommu_ops *iommu_ops;
 	int (*attach_dev)(struct iommu_domain *domain, struct device *dev);
 	void (*detach_dev)(struct iommu_domain *domain, struct device *dev);