In existing code, if wanting to loop all devices attached to a domain,
current code can only loop the devices which are attached to the domain
via normal manner. While for devices attached via auxiliary manner, this
is subdevice, they are not tracked in the domain. This patch adds struct
subdevice_domain_info which is created per domain attachment via auxiliary
manner. So that such devices are also tracked in domain.

This was found by when I'm working on the belwo patch, There is no device
in domain->devices, thus unable to get the cap and ecap of iommu unit. But
this domain actually has one sub-device which is attached via aux-manner.
This patch fixes the issue.

https://lore.kernel.org/kvm/1599734733-6431-17-git-send-email-yi.l....@intel.com/

But looks like, it doesn't affect me only. Such auxiliary track should be
there for example if wanting to flush device_iotlb for a domain which has
devices attached by auxiliray manner, then this fix is also necessary. This
issue will also be fixed by another patch in this series with some additional
changes based on the sudevice tracking framework introduced in this patch.

Co-developed-by: Xin Zeng <xin.z...@intel.com>
Signed-off-by: Xin Zeng <xin.z...@intel.com>
Co-developed-by: Liu Yi L <yi.l....@intel.com>
Signed-off-by: Liu Yi L <yi.l....@intel.com>
Co-developed-by: Lu Baolu <baolu...@linux.intel.com>
Signed-off-by: Lu Baolu <baolu...@linux.intel.com>
---
 drivers/iommu/intel/iommu.c | 92 ++++++++++++++++++++++++++++++++-----
 include/linux/intel-iommu.h | 11 ++++-
 2 files changed, 90 insertions(+), 13 deletions(-)

diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index a49afa11673c..4274b4acc325 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -1881,6 +1881,7 @@ static struct dmar_domain *alloc_domain(int flags)
                domain->flags |= DOMAIN_FLAG_USE_FIRST_LEVEL;
        domain->has_iotlb_device = false;
        INIT_LIST_HEAD(&domain->devices);
+       INIT_LIST_HEAD(&domain->sub_devices);
 
        return domain;
 }
@@ -5172,33 +5173,79 @@ is_aux_domain(struct device *dev, struct iommu_domain 
*domain)
                        domain->type == IOMMU_DOMAIN_UNMANAGED;
 }
 
+static inline
+void _auxiliary_link_device(struct dmar_domain *domain,
+                           struct subdevice_domain_info *subinfo,
+                           struct device *dev)
+{
+       subinfo->users++;
+}
+
+static inline
+int _auxiliary_unlink_device(struct dmar_domain *domain,
+                            struct subdevice_domain_info *subinfo,
+                            struct device *dev)
+{
+       subinfo->users--;
+       return subinfo->users;
+}
+
 static void auxiliary_link_device(struct dmar_domain *domain,
                                  struct device *dev)
 {
        struct device_domain_info *info = get_domain_info(dev);
+       struct subdevice_domain_info *subinfo;
 
        assert_spin_locked(&device_domain_lock);
        if (WARN_ON(!info))
                return;
 
+       subinfo = kzalloc(sizeof(*subinfo), GFP_ATOMIC);
+       if (!subinfo)
+               return;
+
+       subinfo->domain = domain;
+       subinfo->dev = dev;
+       list_add(&subinfo->link_domain, &info->auxiliary_domains);
+       list_add(&subinfo->link_phys, &domain->sub_devices);
+       _auxiliary_link_device(domain, subinfo, dev);
        domain->auxd_refcnt++;
-       list_add(&domain->auxd, &info->auxiliary_domains);
 }
 
-static void auxiliary_unlink_device(struct dmar_domain *domain,
-                                   struct device *dev)
+static struct subdevice_domain_info *
+subdevice_domain_info_lookup(struct dmar_domain *domain, struct device *dev)
+{
+       struct subdevice_domain_info *subinfo;
+
+       assert_spin_locked(&device_domain_lock);
+
+       list_for_each_entry(subinfo, &domain->sub_devices, link_phys)
+               if (subinfo->dev == dev)
+                       return subinfo;
+
+       return NULL;
+}
+
+static int auxiliary_unlink_device(struct dmar_domain *domain,
+                                  struct subdevice_domain_info *subinfo,
+                                  struct device *dev)
 {
        struct device_domain_info *info = get_domain_info(dev);
+       int ret;
 
        assert_spin_locked(&device_domain_lock);
        if (WARN_ON(!info))
-               return;
+               return -EINVAL;
 
-       list_del(&domain->auxd);
+       ret = _auxiliary_unlink_device(domain, subinfo, dev);
+       if (ret == 0) {
+               list_del(&subinfo->link_domain);
+               list_del(&subinfo->link_phys);
+               kfree(subinfo);
+       }
        domain->auxd_refcnt--;
 
-       if (!domain->auxd_refcnt && domain->default_pasid > 0)
-               ioasid_free(domain->default_pasid);
+       return ret;
 }
 
 static int aux_domain_add_dev(struct dmar_domain *domain,
@@ -5207,6 +5254,8 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
        int ret;
        unsigned long flags;
        struct intel_iommu *iommu;
+       struct device_domain_info *info = get_domain_info(dev);
+       struct subdevice_domain_info *subinfo;
 
        iommu = device_to_iommu(dev, NULL, NULL);
        if (!iommu)
@@ -5227,6 +5276,12 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
        }
 
        spin_lock_irqsave(&device_domain_lock, flags);
+       subinfo = subdevice_domain_info_lookup(domain, dev);
+       if (subinfo) {
+               _auxiliary_link_device(domain, subinfo, dev);
+               spin_unlock_irqrestore(&device_domain_lock, flags);
+               return 0;
+       }
        /*
         * iommu->lock must be held to attach domain to iommu and setup the
         * pasid entry for second level translation.
@@ -5270,6 +5325,7 @@ static void aux_domain_remove_dev(struct dmar_domain 
*domain,
        struct device_domain_info *info;
        struct intel_iommu *iommu;
        unsigned long flags;
+       struct subdevice_domain_info *subinfo;
 
        if (!is_aux_domain(dev, &domain->domain))
                return;
@@ -5278,14 +5334,26 @@ static void aux_domain_remove_dev(struct dmar_domain 
*domain,
        info = get_domain_info(dev);
        iommu = info->iommu;
 
-       auxiliary_unlink_device(domain, dev);
+       subinfo = subdevice_domain_info_lookup(domain, dev);
+       if (!subinfo) {
+               spin_unlock_irqrestore(&device_domain_lock, flags);
+               return;
+       }
 
-       spin_lock(&iommu->lock);
-       intel_pasid_tear_down_entry(iommu, dev, domain->default_pasid, false);
-       domain_detach_iommu(domain, iommu);
-       spin_unlock(&iommu->lock);
+       if (auxiliary_unlink_device(domain, subinfo, dev) == 0) {
+               spin_lock(&iommu->lock);
+               intel_pasid_tear_down_entry(iommu,
+                                           dev,
+                                           domain->default_pasid,
+                                           false);
+               domain_detach_iommu(domain, iommu);
+               spin_unlock(&iommu->lock);
+       }
 
        spin_unlock_irqrestore(&device_domain_lock, flags);
+
+       if (!domain->auxd_refcnt && domain->default_pasid > 0)
+               ioasid_free(domain->default_pasid);
 }
 
 static int prepare_domain_attach_device(struct iommu_domain *domain,
diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h
index 94522685a0d9..1fb3d6ab719a 100644
--- a/include/linux/intel-iommu.h
+++ b/include/linux/intel-iommu.h
@@ -537,7 +537,7 @@ struct dmar_domain {
 
        bool has_iotlb_device;
        struct list_head devices;       /* all devices' list */
-       struct list_head auxd;          /* link to device's auxiliary list */
+       struct list_head sub_devices;   /* all devices' list attached via 
aux-attach */
        struct iova_domain iovad;       /* iova's that belong to this domain */
 
        struct dma_pte  *pgd;           /* virtual address */
@@ -636,6 +636,15 @@ struct device_domain_info {
        struct pasid_table *pasid_table; /* pasid table */
 };
 
+/* Aux attach device domain info */
+struct subdevice_domain_info {
+       struct device *dev;
+       struct dmar_domain *domain;
+       struct list_head link_phys;     /* link to phys device siblings */
+       struct list_head link_domain;   /* link to domain siblings */
+       int users;
+};
+
 static inline void __iommu_flush_cache(
        struct intel_iommu *iommu, void *addr, int size)
 {
-- 
2.25.1

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to