This checks whether a domain should use first level page table
for map/unmap. And if so, we should attach the domain to the
device in first level translation mode.

Cc: Ashok Raj <[email protected]>
Cc: Jacob Pan <[email protected]>
Cc: Kevin Tian <[email protected]>
Cc: Liu Yi L <[email protected]>
Cc: Yi Sun <[email protected]>
Cc: Sanjay Kumar <[email protected]>
Signed-off-by: Lu Baolu <[email protected]>
---
 drivers/iommu/intel-iommu.c | 63 +++++++++++++++++++++++++++++++++++--
 1 file changed, 60 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 695a7a5fbe8e..68b2f98ecd65 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -417,6 +417,11 @@ const struct iommu_ops intel_iommu_ops;
 static const struct pgtable_ops first_lvl_pgtable_ops;
 static const struct pgtable_ops second_lvl_pgtable_ops;
 
+static inline bool domain_pgtable_first_lvl(struct dmar_domain *domain)
+{
+       return domain->ops == &first_lvl_pgtable_ops;
+}
+
 static bool translation_pre_enabled(struct intel_iommu *iommu)
 {
        return (iommu->flags & VTD_FLAG_TRANS_PRE_ENABLED);
@@ -1702,6 +1707,44 @@ static bool first_lvl_5lp_support(void)
        return first_level_5lp_supported;
 }
 
+/*
+ * Check and return whether first level is used by default for
+ * DMA translation.
+ */
+static bool first_level_by_default(void)
+{
+       struct dmar_drhd_unit *drhd;
+       struct intel_iommu *iommu;
+       static int first_level_support = -1;
+
+       if (likely(first_level_support != -1))
+               return first_level_support;
+
+       first_level_support = 1;
+
+       rcu_read_lock();
+       for_each_active_iommu(iommu, drhd) {
+               if (!sm_supported(iommu) || !ecap_flts(iommu->ecap)) {
+                       first_level_support = 0;
+                       break;
+               }
+#ifdef CONFIG_X86
+               /*
+                * Currently we don't support paging mode missmatching.
+                * Could be turned on later if there is a case.
+                */
+               if (cpu_feature_enabled(X86_FEATURE_LA57) &&
+                   !cap_5lp_support(iommu->cap)) {
+                       first_level_support = 0;
+                       break;
+               }
+#endif /* #ifdef CONFIG_X86 */
+       }
+       rcu_read_unlock();
+
+       return first_level_support;
+}
+
 static struct dmar_domain *alloc_domain(int flags)
 {
        struct dmar_domain *domain;
@@ -1714,7 +1757,10 @@ static struct dmar_domain *alloc_domain(int flags)
        domain->nid = NUMA_NO_NODE;
        domain->flags = flags;
        domain->has_iotlb_device = false;
-       domain->ops = &second_lvl_pgtable_ops;
+       if (first_level_by_default())
+               domain->ops = &first_lvl_pgtable_ops;
+       else
+               domain->ops = &second_lvl_pgtable_ops;
        domain->first_lvl_5lp = first_lvl_5lp_support();
        spin_lock_init(&domain->page_table_lock);
        INIT_LIST_HEAD(&domain->devices);
@@ -2710,6 +2756,11 @@ static struct dmar_domain 
*dmar_insert_one_dev_info(struct intel_iommu *iommu,
                if (hw_pass_through && domain_type_is_si(domain))
                        ret = intel_pasid_setup_pass_through(iommu, domain,
                                        dev, PASID_RID2PASID);
+               else if (domain_pgtable_first_lvl(domain))
+                       ret = intel_pasid_setup_first_level(iommu, dev,
+                                       domain->pgd, PASID_RID2PASID,
+                                       domain->iommu_did[iommu->seq_id],
+                                       PASID_FLAG_SUPERVISOR_MODE);
                else
                        ret = intel_pasid_setup_second_level(iommu, domain,
                                        dev, PASID_RID2PASID);
@@ -5597,8 +5648,14 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
                goto attach_failed;
 
        /* Setup the PASID entry for mediated devices: */
-       ret = intel_pasid_setup_second_level(iommu, domain, dev,
-                                            domain->default_pasid);
+       if (domain_pgtable_first_lvl(domain))
+               ret = intel_pasid_setup_first_level(iommu, dev,
+                               domain->pgd, domain->default_pasid,
+                               domain->iommu_did[iommu->seq_id],
+                               PASID_FLAG_SUPERVISOR_MODE);
+       else
+               ret = intel_pasid_setup_second_level(iommu, domain, dev,
+                                                    domain->default_pasid);
        if (ret)
                goto table_failed;
        spin_unlock(&iommu->lock);
-- 
2.17.1

_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to