Re: [RFC PATCH 3/4] iommu/vt-d: Map/unmap domain with mmmap/mmunmap

From: Lu Baolu
Date: Wed Sep 25 2019 - 03:08:46 EST


Hi,

On 9/25/19 1:00 PM, Tian, Kevin wrote:
From: Lu Baolu [mailto:baolu.lu@xxxxxxxxxxxxxxx]
Sent: Monday, September 23, 2019 8:25 PM

If a dmar domain has DOMAIN_FLAG_FIRST_LEVEL_TRANS bit set
in its flags, IOMMU will use the first level page table for
translation. Hence, we need to map or unmap addresses in the
first level page table.

Cc: Ashok Raj <ashok.raj@xxxxxxxxx>
Cc: Jacob Pan <jacob.jun.pan@xxxxxxxxxxxxxxx>
Cc: Kevin Tian <kevin.tian@xxxxxxxxx>
Cc: Liu Yi L <yi.l.liu@xxxxxxxxx>
Cc: Yi Sun <yi.y.sun@xxxxxxxxxxxxxxx>
Signed-off-by: Lu Baolu <baolu.lu@xxxxxxxxxxxxxxx>
---
drivers/iommu/intel-iommu.c | 94 ++++++++++++++++++++++++++++++++-
----
1 file changed, 82 insertions(+), 12 deletions(-)

diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 9cfe8098d993..103480016010 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -168,6 +168,11 @@ static inline unsigned long virt_to_dma_pfn(void
*p)
return page_to_dma_pfn(virt_to_page(p));
}

+static inline unsigned long dma_pfn_to_addr(unsigned long pfn)
+{
+ return pfn << VTD_PAGE_SHIFT;
+}
+
/* global iommu list, set NULL for ignored DMAR units */
static struct intel_iommu **g_iommus;

@@ -307,6 +312,9 @@ static int hw_pass_through = 1;
*/
#define DOMAIN_FLAG_LOSE_CHILDREN BIT(1)

+/* Domain uses first level translation for DMA remapping. */
+#define DOMAIN_FLAG_FIRST_LEVEL_TRANS BIT(2)
+
#define for_each_domain_iommu(idx, domain) \
for (idx = 0; idx < g_num_of_iommus; idx++) \
if (domain->iommu_refcnt[idx])
@@ -552,6 +560,11 @@ static inline int domain_type_is_si(struct
dmar_domain *domain)
return domain->flags & DOMAIN_FLAG_STATIC_IDENTITY;
}

+static inline int domain_type_is_flt(struct dmar_domain *domain)
+{
+ return domain->flags & DOMAIN_FLAG_FIRST_LEVEL_TRANS;
+}
+
static inline int domain_pfn_supported(struct dmar_domain *domain,
unsigned long pfn)
{
@@ -1147,8 +1160,15 @@ static struct page *domain_unmap(struct
dmar_domain *domain,
BUG_ON(start_pfn > last_pfn);

/* we don't need lock here; nobody else touches the iova range */
- freelist = dma_pte_clear_level(domain, agaw_to_level(domain-
agaw),
- domain->pgd, 0, start_pfn, last_pfn,
NULL);
+ if (domain_type_is_flt(domain))
+ freelist = intel_mmunmap_range(domain,
+ dma_pfn_to_addr(start_pfn),
+ dma_pfn_to_addr(last_pfn + 1));
+ else
+ freelist = dma_pte_clear_level(domain,
+ agaw_to_level(domain->agaw),
+ domain->pgd, 0, start_pfn,
+ last_pfn, NULL);

what about providing an unified interface at the caller side, then having
the level differentiated within the interface?

Good point! I ever thought about adding some ops in struct dmar_domain,
something like:

diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h
index ed11ef594378..1dd184f76bfb 100644
--- a/include/linux/intel-iommu.h
+++ b/include/linux/intel-iommu.h
@@ -489,7 +489,14 @@ struct dmar_domain {
struct list_head auxd; /* link to device's auxiliary list */
struct iova_domain iovad; /* iova's that belong to this domain */

+ /* per domain page table and manipulation ops */
struct dma_pte *pgd; /* virtual address */
+ int (*map)(struct dmar_domain *domain,
+ unsigned long addr, unsigned long end,
+ phys_addr_t phys_addr, int dma_prot);
+ struct page *(*unmap)(struct dmar_domain *domain,
+ unsigned long addr, unsigned long end);
+
int gaw; /* max guest address width */

/* adjusted guest address width, 0 is level 2 30-bit */

So that this code could be simply like this:

freelist = domain->unmap(...);

Best regards,
Baolu