* [PATCH 01/10] iommu/vt-d: Track nested domains in parent
2024-02-19 11:15 [PATCH 00/10] [PULL REQUEST] iommu/vt-d: Fixes for v6.8-rc5 Lu Baolu
@ 2024-02-19 11:15 ` Lu Baolu
2024-02-19 11:15 ` [PATCH 02/10] iommu/vt-d: Add __iommu_flush_iotlb_psi() Lu Baolu
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Lu Baolu @ 2024-02-19 11:15 UTC (permalink / raw)
To: Joerg Roedel
Cc: Yi Liu, Arnd Bergmann, Kevin Tian, Joao Martins, iommu,
linux-kernel
From: Yi Liu <yi.l.liu@intel.com>
Today the parent domain (s2_domain) is unaware of which DID's are
used by and which devices are attached to nested domains (s1_domain)
nested on it. This leads to a problem that some operations (flush
iotlb/devtlb and enable dirty tracking) on parent domain only apply to
DID's and devices directly tracked in the parent domain hence are
incomplete.
This tracks the nested domains in list in parent domain. With this,
operations on parent domain can loop the nested domains and refer to
the devices and iommu_array to ensure the operations on parent domain
take effect on all the affected devices and iommus.
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Link: https://lore.kernel.org/r/20240208082307.15759-2-yi.l.liu@intel.com
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
drivers/iommu/intel/iommu.h | 6 ++++++
drivers/iommu/intel/iommu.c | 18 ++++++++++++++----
drivers/iommu/intel/nested.c | 12 +++++++++++-
3 files changed, 31 insertions(+), 5 deletions(-)
diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h
index d02f916d8e59..9b27edb73aa9 100644
--- a/drivers/iommu/intel/iommu.h
+++ b/drivers/iommu/intel/iommu.h
@@ -627,6 +627,10 @@ struct dmar_domain {
int agaw;
/* maximum mapped address */
u64 max_addr;
+ /* Protect the s1_domains list */
+ spinlock_t s1_lock;
+ /* Track s1_domains nested on this domain */
+ struct list_head s1_domains;
};
/* Nested user domain */
@@ -637,6 +641,8 @@ struct dmar_domain {
unsigned long s1_pgtbl;
/* page table attributes */
struct iommu_hwpt_vtd_s1 s1_cfg;
+ /* link to parent domain siblings */
+ struct list_head s2_link;
};
};
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 6fb5f6fceea1..e393c62776f3 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -3883,6 +3883,7 @@ intel_iommu_domain_alloc_user(struct device *dev, u32 flags,
bool dirty_tracking = flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
bool nested_parent = flags & IOMMU_HWPT_ALLOC_NEST_PARENT;
struct intel_iommu *iommu = info->iommu;
+ struct dmar_domain *dmar_domain;
struct iommu_domain *domain;
/* Must be NESTING domain */
@@ -3908,11 +3909,16 @@ intel_iommu_domain_alloc_user(struct device *dev, u32 flags,
if (!domain)
return ERR_PTR(-ENOMEM);
- if (nested_parent)
- to_dmar_domain(domain)->nested_parent = true;
+ dmar_domain = to_dmar_domain(domain);
+
+ if (nested_parent) {
+ dmar_domain->nested_parent = true;
+ INIT_LIST_HEAD(&dmar_domain->s1_domains);
+ spin_lock_init(&dmar_domain->s1_lock);
+ }
if (dirty_tracking) {
- if (to_dmar_domain(domain)->use_first_level) {
+ if (dmar_domain->use_first_level) {
iommu_domain_free(domain);
return ERR_PTR(-EOPNOTSUPP);
}
@@ -3924,8 +3930,12 @@ intel_iommu_domain_alloc_user(struct device *dev, u32 flags,
static void intel_iommu_domain_free(struct iommu_domain *domain)
{
+ struct dmar_domain *dmar_domain = to_dmar_domain(domain);
+
+ WARN_ON(dmar_domain->nested_parent &&
+ !list_empty(&dmar_domain->s1_domains));
if (domain != &si_domain->domain)
- domain_exit(to_dmar_domain(domain));
+ domain_exit(dmar_domain);
}
int prepare_domain_attach_device(struct iommu_domain *domain,
diff --git a/drivers/iommu/intel/nested.c b/drivers/iommu/intel/nested.c
index f26c7f1c46cc..6a75f6eb18f1 100644
--- a/drivers/iommu/intel/nested.c
+++ b/drivers/iommu/intel/nested.c
@@ -70,7 +70,13 @@ static int intel_nested_attach_dev(struct iommu_domain *domain,
static void intel_nested_domain_free(struct iommu_domain *domain)
{
- kfree(to_dmar_domain(domain));
+ struct dmar_domain *dmar_domain = to_dmar_domain(domain);
+ struct dmar_domain *s2_domain = dmar_domain->s2_domain;
+
+ spin_lock(&s2_domain->s1_lock);
+ list_del(&dmar_domain->s2_link);
+ spin_unlock(&s2_domain->s1_lock);
+ kfree(dmar_domain);
}
static void nested_flush_dev_iotlb(struct dmar_domain *domain, u64 addr,
@@ -201,5 +207,9 @@ struct iommu_domain *intel_nested_domain_alloc(struct iommu_domain *parent,
spin_lock_init(&domain->lock);
xa_init(&domain->iommu_array);
+ spin_lock(&s2_domain->s1_lock);
+ list_add(&domain->s2_link, &s2_domain->s1_domains);
+ spin_unlock(&s2_domain->s1_lock);
+
return &domain->domain;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 02/10] iommu/vt-d: Add __iommu_flush_iotlb_psi()
2024-02-19 11:15 [PATCH 00/10] [PULL REQUEST] iommu/vt-d: Fixes for v6.8-rc5 Lu Baolu
2024-02-19 11:15 ` [PATCH 01/10] iommu/vt-d: Track nested domains in parent Lu Baolu
@ 2024-02-19 11:15 ` Lu Baolu
2024-02-19 11:15 ` [PATCH 03/10] iommu/vt-d: Add missing iotlb flush for parent domain Lu Baolu
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Lu Baolu @ 2024-02-19 11:15 UTC (permalink / raw)
To: Joerg Roedel
Cc: Yi Liu, Arnd Bergmann, Kevin Tian, Joao Martins, iommu,
linux-kernel
From: Yi Liu <yi.l.liu@intel.com>
Add __iommu_flush_iotlb_psi() to do the psi iotlb flush with a DID input
rather than calculating it within the helper.
This is useful when flushing cache for parent domain which reuses DIDs of
its nested domains.
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Link: https://lore.kernel.org/r/20240208082307.15759-3-yi.l.liu@intel.com
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
drivers/iommu/intel/iommu.c | 78 ++++++++++++++++++++-----------------
1 file changed, 43 insertions(+), 35 deletions(-)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index e393c62776f3..dbdb8366c42a 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -1368,6 +1368,46 @@ static void domain_flush_pasid_iotlb(struct intel_iommu *iommu,
spin_unlock_irqrestore(&domain->lock, flags);
}
+static void __iommu_flush_iotlb_psi(struct intel_iommu *iommu, u16 did,
+ unsigned long pfn, unsigned int pages,
+ int ih)
+{
+ unsigned int aligned_pages = __roundup_pow_of_two(pages);
+ unsigned long bitmask = aligned_pages - 1;
+ unsigned int mask = ilog2(aligned_pages);
+ u64 addr = (u64)pfn << VTD_PAGE_SHIFT;
+
+ /*
+ * PSI masks the low order bits of the base address. If the
+ * address isn't aligned to the mask, then compute a mask value
+ * needed to ensure the target range is flushed.
+ */
+ if (unlikely(bitmask & pfn)) {
+ unsigned long end_pfn = pfn + pages - 1, shared_bits;
+
+ /*
+ * Since end_pfn <= pfn + bitmask, the only way bits
+ * higher than bitmask can differ in pfn and end_pfn is
+ * by carrying. This means after masking out bitmask,
+ * high bits starting with the first set bit in
+ * shared_bits are all equal in both pfn and end_pfn.
+ */
+ shared_bits = ~(pfn ^ end_pfn) & ~bitmask;
+ mask = shared_bits ? __ffs(shared_bits) : BITS_PER_LONG;
+ }
+
+ /*
+ * Fallback to domain selective flush if no PSI support or
+ * the size is too big.
+ */
+ if (!cap_pgsel_inv(iommu->cap) || mask > cap_max_amask_val(iommu->cap))
+ iommu->flush.flush_iotlb(iommu, did, 0, 0,
+ DMA_TLB_DSI_FLUSH);
+ else
+ iommu->flush.flush_iotlb(iommu, did, addr | ih, mask,
+ DMA_TLB_PSI_FLUSH);
+}
+
static void iommu_flush_iotlb_psi(struct intel_iommu *iommu,
struct dmar_domain *domain,
unsigned long pfn, unsigned int pages,
@@ -1384,42 +1424,10 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu,
if (ih)
ih = 1 << 6;
- if (domain->use_first_level) {
+ if (domain->use_first_level)
domain_flush_pasid_iotlb(iommu, domain, addr, pages, ih);
- } else {
- unsigned long bitmask = aligned_pages - 1;
-
- /*
- * PSI masks the low order bits of the base address. If the
- * address isn't aligned to the mask, then compute a mask value
- * needed to ensure the target range is flushed.
- */
- if (unlikely(bitmask & pfn)) {
- unsigned long end_pfn = pfn + pages - 1, shared_bits;
-
- /*
- * Since end_pfn <= pfn + bitmask, the only way bits
- * higher than bitmask can differ in pfn and end_pfn is
- * by carrying. This means after masking out bitmask,
- * high bits starting with the first set bit in
- * shared_bits are all equal in both pfn and end_pfn.
- */
- shared_bits = ~(pfn ^ end_pfn) & ~bitmask;
- mask = shared_bits ? __ffs(shared_bits) : BITS_PER_LONG;
- }
-
- /*
- * Fallback to domain selective flush if no PSI support or
- * the size is too big.
- */
- if (!cap_pgsel_inv(iommu->cap) ||
- mask > cap_max_amask_val(iommu->cap))
- iommu->flush.flush_iotlb(iommu, did, 0, 0,
- DMA_TLB_DSI_FLUSH);
- else
- iommu->flush.flush_iotlb(iommu, did, addr | ih, mask,
- DMA_TLB_PSI_FLUSH);
- }
+ else
+ __iommu_flush_iotlb_psi(iommu, did, pfn, pages, ih);
/*
* In caching mode, changes of pages from non-present to present require
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 03/10] iommu/vt-d: Add missing iotlb flush for parent domain
2024-02-19 11:15 [PATCH 00/10] [PULL REQUEST] iommu/vt-d: Fixes for v6.8-rc5 Lu Baolu
2024-02-19 11:15 ` [PATCH 01/10] iommu/vt-d: Track nested domains in parent Lu Baolu
2024-02-19 11:15 ` [PATCH 02/10] iommu/vt-d: Add __iommu_flush_iotlb_psi() Lu Baolu
@ 2024-02-19 11:15 ` Lu Baolu
2024-02-19 11:15 ` [PATCH 04/10] iommu/vt-d: Update iotlb in nested domain attach Lu Baolu
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Lu Baolu @ 2024-02-19 11:15 UTC (permalink / raw)
To: Joerg Roedel
Cc: Yi Liu, Arnd Bergmann, Kevin Tian, Joao Martins, iommu,
linux-kernel
From: Yi Liu <yi.l.liu@intel.com>
If a domain is used as the parent in nested translation its mappings might
be cached using DID of the nested domain. But the existing code ignores
this fact to only invalidate the iotlb entries tagged by the domain's own
DID.
Loop the s1_domains list, if any, to invalidate all iotlb entries related
to the target s2 address range. According to VT-d spec there is no need for
software to explicitly flush the affected s1 cache. It's implicitly done by
HW when s2 cache is invalidated.
Fixes: b41e38e22539 ("iommu/vt-d: Add nested domain allocation")
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Link: https://lore.kernel.org/r/20240208082307.15759-4-yi.l.liu@intel.com
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
drivers/iommu/intel/iommu.c | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index dbdb8366c42a..e3dbcae95975 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -1451,6 +1451,28 @@ static void __mapping_notify_one(struct intel_iommu *iommu, struct dmar_domain *
iommu_flush_write_buffer(iommu);
}
+/*
+ * Flush the relevant caches in nested translation if the domain
+ * also serves as a parent
+ */
+static void parent_domain_flush(struct dmar_domain *domain,
+ unsigned long pfn,
+ unsigned long pages, int ih)
+{
+ struct dmar_domain *s1_domain;
+
+ spin_lock(&domain->s1_lock);
+ list_for_each_entry(s1_domain, &domain->s1_domains, s2_link) {
+ struct iommu_domain_info *info;
+ unsigned long i;
+
+ xa_for_each(&s1_domain->iommu_array, i, info)
+ __iommu_flush_iotlb_psi(info->iommu, info->did,
+ pfn, pages, ih);
+ }
+ spin_unlock(&domain->s1_lock);
+}
+
static void intel_flush_iotlb_all(struct iommu_domain *domain)
{
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
@@ -1470,6 +1492,9 @@ static void intel_flush_iotlb_all(struct iommu_domain *domain)
if (!cap_caching_mode(iommu->cap))
iommu_flush_dev_iotlb(dmar_domain, 0, MAX_AGAW_PFN_WIDTH);
}
+
+ if (dmar_domain->nested_parent)
+ parent_domain_flush(dmar_domain, 0, -1, 0);
}
static void iommu_disable_protect_mem_regions(struct intel_iommu *iommu)
@@ -1993,6 +2018,9 @@ static void switch_to_super_page(struct dmar_domain *domain,
iommu_flush_iotlb_psi(info->iommu, domain,
start_pfn, lvl_pages,
0, 0);
+ if (domain->nested_parent)
+ parent_domain_flush(domain, start_pfn,
+ lvl_pages, 0);
}
pte++;
@@ -4125,6 +4153,9 @@ static void intel_iommu_tlb_sync(struct iommu_domain *domain,
start_pfn, nrpages,
list_empty(&gather->freelist), 0);
+ if (dmar_domain->nested_parent)
+ parent_domain_flush(dmar_domain, start_pfn, nrpages,
+ list_empty(&gather->freelist));
put_pages_list(&gather->freelist);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 04/10] iommu/vt-d: Update iotlb in nested domain attach
2024-02-19 11:15 [PATCH 00/10] [PULL REQUEST] iommu/vt-d: Fixes for v6.8-rc5 Lu Baolu
` (2 preceding siblings ...)
2024-02-19 11:15 ` [PATCH 03/10] iommu/vt-d: Add missing iotlb flush for parent domain Lu Baolu
@ 2024-02-19 11:15 ` Lu Baolu
2024-02-19 11:15 ` [PATCH 05/10] iommu/vt-d: Add missing device iotlb flush for parent domain Lu Baolu
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Lu Baolu @ 2024-02-19 11:15 UTC (permalink / raw)
To: Joerg Roedel
Cc: Yi Liu, Arnd Bergmann, Kevin Tian, Joao Martins, iommu,
linux-kernel
From: Yi Liu <yi.l.liu@intel.com>
Should call domain_update_iotlb() to update the has_iotlb_device flag
of the domain after attaching device to nested domain. Without it, this
flag is not set properly and would result in missing device TLB flush.
Fixes: 9838f2bb6b6b ("iommu/vt-d: Set the nested domain to a device")
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Link: https://lore.kernel.org/r/20240208082307.15759-5-yi.l.liu@intel.com
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
drivers/iommu/intel/iommu.h | 1 +
drivers/iommu/intel/iommu.c | 4 +---
drivers/iommu/intel/nested.c | 2 ++
3 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h
index 9b27edb73aa9..4145c04cb1c6 100644
--- a/drivers/iommu/intel/iommu.h
+++ b/drivers/iommu/intel/iommu.h
@@ -1066,6 +1066,7 @@ int qi_submit_sync(struct intel_iommu *iommu, struct qi_desc *desc,
*/
#define QI_OPT_WAIT_DRAIN BIT(0)
+void domain_update_iotlb(struct dmar_domain *domain);
int domain_attach_iommu(struct dmar_domain *domain, struct intel_iommu *iommu);
void domain_detach_iommu(struct dmar_domain *domain, struct intel_iommu *iommu);
void device_block_translation(struct device *dev);
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index e3dbcae95975..711c3e3fe095 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -396,8 +396,6 @@ static int domain_update_device_node(struct dmar_domain *domain)
return nid;
}
-static void domain_update_iotlb(struct dmar_domain *domain);
-
/* Return the super pagesize bitmap if supported. */
static unsigned long domain_super_pgsize_bitmap(struct dmar_domain *domain)
{
@@ -1218,7 +1216,7 @@ domain_lookup_dev_info(struct dmar_domain *domain,
return NULL;
}
-static void domain_update_iotlb(struct dmar_domain *domain)
+void domain_update_iotlb(struct dmar_domain *domain)
{
struct dev_pasid_info *dev_pasid;
struct device_domain_info *info;
diff --git a/drivers/iommu/intel/nested.c b/drivers/iommu/intel/nested.c
index 6a75f6eb18f1..d5af5925a31c 100644
--- a/drivers/iommu/intel/nested.c
+++ b/drivers/iommu/intel/nested.c
@@ -65,6 +65,8 @@ static int intel_nested_attach_dev(struct iommu_domain *domain,
list_add(&info->link, &dmar_domain->devices);
spin_unlock_irqrestore(&dmar_domain->lock, flags);
+ domain_update_iotlb(dmar_domain);
+
return 0;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 05/10] iommu/vt-d: Add missing device iotlb flush for parent domain
2024-02-19 11:15 [PATCH 00/10] [PULL REQUEST] iommu/vt-d: Fixes for v6.8-rc5 Lu Baolu
` (3 preceding siblings ...)
2024-02-19 11:15 ` [PATCH 04/10] iommu/vt-d: Update iotlb in nested domain attach Lu Baolu
@ 2024-02-19 11:15 ` Lu Baolu
2024-02-19 11:15 ` [PATCH 06/10] iommu/vt-d: Remove domain parameter for intel_pasid_setup_dirty_tracking() Lu Baolu
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Lu Baolu @ 2024-02-19 11:15 UTC (permalink / raw)
To: Joerg Roedel
Cc: Yi Liu, Arnd Bergmann, Kevin Tian, Joao Martins, iommu,
linux-kernel
From: Yi Liu <yi.l.liu@intel.com>
ATS-capable devices cache the result of nested translation. This result
relies on the mappings in s2 domain (a.k.a. parent). When there are
modifications in the s2 domain, the related nested translation caches on
the device should be flushed. This includes the devices that are attached
to the s1 domain. However, the existing code ignores this fact to only
loops its own devices.
As there is no easy way to identify the exact set of nested translations
affected by the change of s2 domain. So, this just flushes the entire
device iotlb on the device.
As above, driver loops the s2 domain's s1_domains list and loops the
devices list of each s1_domain to flush the entire device iotlb on the
devices.
Fixes: b41e38e22539 ("iommu/vt-d: Add nested domain allocation")
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Link: https://lore.kernel.org/r/20240208082307.15759-6-yi.l.liu@intel.com
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
drivers/iommu/intel/iommu.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 711c3e3fe095..2eee83b5441b 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -1461,12 +1461,30 @@ static void parent_domain_flush(struct dmar_domain *domain,
spin_lock(&domain->s1_lock);
list_for_each_entry(s1_domain, &domain->s1_domains, s2_link) {
+ struct device_domain_info *device_info;
struct iommu_domain_info *info;
+ unsigned long flags;
unsigned long i;
xa_for_each(&s1_domain->iommu_array, i, info)
__iommu_flush_iotlb_psi(info->iommu, info->did,
pfn, pages, ih);
+
+ if (!s1_domain->has_iotlb_device)
+ continue;
+
+ spin_lock_irqsave(&s1_domain->lock, flags);
+ list_for_each_entry(device_info, &s1_domain->devices, link)
+ /*
+ * Address translation cache in device side caches the
+ * result of nested translation. There is no easy way
+ * to identify the exact set of nested translations
+ * affected by a change in S2. So just flush the entire
+ * device cache.
+ */
+ __iommu_flush_dev_iotlb(device_info, 0,
+ MAX_AGAW_PFN_WIDTH);
+ spin_unlock_irqrestore(&s1_domain->lock, flags);
}
spin_unlock(&domain->s1_lock);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 06/10] iommu/vt-d: Remove domain parameter for intel_pasid_setup_dirty_tracking()
2024-02-19 11:15 [PATCH 00/10] [PULL REQUEST] iommu/vt-d: Fixes for v6.8-rc5 Lu Baolu
` (4 preceding siblings ...)
2024-02-19 11:15 ` [PATCH 05/10] iommu/vt-d: Add missing device iotlb flush for parent domain Lu Baolu
@ 2024-02-19 11:15 ` Lu Baolu
2024-02-19 11:15 ` [PATCH 07/10] iommu/vt-d: Wrap the dirty tracking loop to be a helper Lu Baolu
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Lu Baolu @ 2024-02-19 11:15 UTC (permalink / raw)
To: Joerg Roedel
Cc: Yi Liu, Arnd Bergmann, Kevin Tian, Joao Martins, iommu,
linux-kernel
From: Yi Liu <yi.l.liu@intel.com>
The only usage of input @domain is to get the domain id (DID) to flush
cache after setting dirty tracking. However, DID can be obtained from
the pasid entry. So no need to pass in domain. This can make this helper
cleaner when adding the missing dirty tracking for the parent domain,
which needs to use the DID of nested domain.
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Joao Martins <joao.m.martins@oracle.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Link: https://lore.kernel.org/r/20240208082307.15759-7-yi.l.liu@intel.com
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
drivers/iommu/intel/pasid.h | 1 -
drivers/iommu/intel/iommu.c | 7 +++----
drivers/iommu/intel/pasid.c | 3 +--
3 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/drivers/iommu/intel/pasid.h b/drivers/iommu/intel/pasid.h
index 8d40d4c66e31..487ede039bdd 100644
--- a/drivers/iommu/intel/pasid.h
+++ b/drivers/iommu/intel/pasid.h
@@ -307,7 +307,6 @@ int intel_pasid_setup_second_level(struct intel_iommu *iommu,
struct dmar_domain *domain,
struct device *dev, u32 pasid);
int intel_pasid_setup_dirty_tracking(struct intel_iommu *iommu,
- struct dmar_domain *domain,
struct device *dev, u32 pasid,
bool enabled);
int intel_pasid_setup_pass_through(struct intel_iommu *iommu,
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 2eee83b5441b..d286b8554227 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -4741,8 +4741,7 @@ static int intel_iommu_set_dirty_tracking(struct iommu_domain *domain,
goto out_unlock;
list_for_each_entry(info, &dmar_domain->devices, link) {
- ret = intel_pasid_setup_dirty_tracking(info->iommu,
- info->domain, info->dev,
+ ret = intel_pasid_setup_dirty_tracking(info->iommu, info->dev,
IOMMU_NO_PASID, enable);
if (ret)
goto err_unwind;
@@ -4756,8 +4755,8 @@ static int intel_iommu_set_dirty_tracking(struct iommu_domain *domain,
err_unwind:
list_for_each_entry(info, &dmar_domain->devices, link)
- intel_pasid_setup_dirty_tracking(info->iommu, dmar_domain,
- info->dev, IOMMU_NO_PASID,
+ intel_pasid_setup_dirty_tracking(info->iommu, info->dev,
+ IOMMU_NO_PASID,
dmar_domain->dirty_tracking);
spin_unlock(&dmar_domain->lock);
return ret;
diff --git a/drivers/iommu/intel/pasid.c b/drivers/iommu/intel/pasid.c
index 3239cefa4c33..a32d7e509842 100644
--- a/drivers/iommu/intel/pasid.c
+++ b/drivers/iommu/intel/pasid.c
@@ -428,7 +428,6 @@ int intel_pasid_setup_second_level(struct intel_iommu *iommu,
* Set up dirty tracking on a second only or nested translation type.
*/
int intel_pasid_setup_dirty_tracking(struct intel_iommu *iommu,
- struct dmar_domain *domain,
struct device *dev, u32 pasid,
bool enabled)
{
@@ -445,7 +444,7 @@ int intel_pasid_setup_dirty_tracking(struct intel_iommu *iommu,
return -ENODEV;
}
- did = domain_id_iommu(domain, iommu);
+ did = pasid_get_domain_id(pte);
pgtt = pasid_pte_get_pgtt(pte);
if (pgtt != PASID_ENTRY_PGTT_SL_ONLY &&
pgtt != PASID_ENTRY_PGTT_NESTED) {
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 07/10] iommu/vt-d: Wrap the dirty tracking loop to be a helper
2024-02-19 11:15 [PATCH 00/10] [PULL REQUEST] iommu/vt-d: Fixes for v6.8-rc5 Lu Baolu
` (5 preceding siblings ...)
2024-02-19 11:15 ` [PATCH 06/10] iommu/vt-d: Remove domain parameter for intel_pasid_setup_dirty_tracking() Lu Baolu
@ 2024-02-19 11:15 ` Lu Baolu
2024-02-19 11:15 ` [PATCH 08/10] iommu/vt-d: Add missing dirty tracking set for parent domain Lu Baolu
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Lu Baolu @ 2024-02-19 11:15 UTC (permalink / raw)
To: Joerg Roedel
Cc: Yi Liu, Arnd Bergmann, Kevin Tian, Joao Martins, iommu,
linux-kernel
From: Yi Liu <yi.l.liu@intel.com>
Add device_set_dirty_tracking() to loop all the devices and set the dirty
tracking per the @enable parameter.
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Reviewed-by: Joao Martins <joao.m.martins@oracle.com>
Link: https://lore.kernel.org/r/20240208082307.15759-8-yi.l.liu@intel.com
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
drivers/iommu/intel/iommu.c | 35 ++++++++++++++++++++++++-----------
1 file changed, 24 insertions(+), 11 deletions(-)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index d286b8554227..2ad8fbe6dc21 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -4729,23 +4729,38 @@ static void *intel_iommu_hw_info(struct device *dev, u32 *length, u32 *type)
return vtd;
}
+/*
+ * Set dirty tracking for the device list of a domain. The caller must
+ * hold the domain->lock when calling it.
+ */
+static int device_set_dirty_tracking(struct list_head *devices, bool enable)
+{
+ struct device_domain_info *info;
+ int ret = 0;
+
+ list_for_each_entry(info, devices, link) {
+ ret = intel_pasid_setup_dirty_tracking(info->iommu, info->dev,
+ IOMMU_NO_PASID, enable);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
static int intel_iommu_set_dirty_tracking(struct iommu_domain *domain,
bool enable)
{
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
- struct device_domain_info *info;
int ret;
spin_lock(&dmar_domain->lock);
if (dmar_domain->dirty_tracking == enable)
goto out_unlock;
- list_for_each_entry(info, &dmar_domain->devices, link) {
- ret = intel_pasid_setup_dirty_tracking(info->iommu, info->dev,
- IOMMU_NO_PASID, enable);
- if (ret)
- goto err_unwind;
- }
+ ret = device_set_dirty_tracking(&dmar_domain->devices, enable);
+ if (ret)
+ goto err_unwind;
dmar_domain->dirty_tracking = enable;
out_unlock:
@@ -4754,10 +4769,8 @@ static int intel_iommu_set_dirty_tracking(struct iommu_domain *domain,
return 0;
err_unwind:
- list_for_each_entry(info, &dmar_domain->devices, link)
- intel_pasid_setup_dirty_tracking(info->iommu, info->dev,
- IOMMU_NO_PASID,
- dmar_domain->dirty_tracking);
+ device_set_dirty_tracking(&dmar_domain->devices,
+ dmar_domain->dirty_tracking);
spin_unlock(&dmar_domain->lock);
return ret;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 08/10] iommu/vt-d: Add missing dirty tracking set for parent domain
2024-02-19 11:15 [PATCH 00/10] [PULL REQUEST] iommu/vt-d: Fixes for v6.8-rc5 Lu Baolu
` (6 preceding siblings ...)
2024-02-19 11:15 ` [PATCH 07/10] iommu/vt-d: Wrap the dirty tracking loop to be a helper Lu Baolu
@ 2024-02-19 11:15 ` Lu Baolu
2024-02-19 11:16 ` [PATCH 09/10] iommu/vt-d: Set SSADE when attaching to a parent with dirty tracking Lu Baolu
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Lu Baolu @ 2024-02-19 11:15 UTC (permalink / raw)
To: Joerg Roedel
Cc: Yi Liu, Arnd Bergmann, Kevin Tian, Joao Martins, iommu,
linux-kernel
From: Yi Liu <yi.l.liu@intel.com>
Setting dirty tracking for a s2 domain requires to loop all the related
devices and set the dirty tracking enable bit in the PASID table entry.
This includes the devices that are attached to the nested domains of a
s2 domain if this s2 domain is used as parent. However, the existing dirty
tracking set only loops s2 domain's own devices. It will miss dirty page
logs in the parent domain.
Now, the parent domain tracks the nested domains, so it can loop the
nested domains and the devices attached to the nested domains to ensure
dirty tracking on the parent is set completely.
Fixes: b41e38e22539 ("iommu/vt-d: Add nested domain allocation")
Signed-off-by: Yi Sun <yi.y.sun@linux.intel.com>
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Link: https://lore.kernel.org/r/20240208082307.15759-9-yi.l.liu@intel.com
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
drivers/iommu/intel/iommu.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 2ad8fbe6dc21..11652e0bcab3 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -4748,6 +4748,35 @@ static int device_set_dirty_tracking(struct list_head *devices, bool enable)
return ret;
}
+static int parent_domain_set_dirty_tracking(struct dmar_domain *domain,
+ bool enable)
+{
+ struct dmar_domain *s1_domain;
+ unsigned long flags;
+ int ret;
+
+ spin_lock(&domain->s1_lock);
+ list_for_each_entry(s1_domain, &domain->s1_domains, s2_link) {
+ spin_lock_irqsave(&s1_domain->lock, flags);
+ ret = device_set_dirty_tracking(&s1_domain->devices, enable);
+ spin_unlock_irqrestore(&s1_domain->lock, flags);
+ if (ret)
+ goto err_unwind;
+ }
+ spin_unlock(&domain->s1_lock);
+ return 0;
+
+err_unwind:
+ list_for_each_entry(s1_domain, &domain->s1_domains, s2_link) {
+ spin_lock_irqsave(&s1_domain->lock, flags);
+ device_set_dirty_tracking(&s1_domain->devices,
+ domain->dirty_tracking);
+ spin_unlock_irqrestore(&s1_domain->lock, flags);
+ }
+ spin_unlock(&domain->s1_lock);
+ return ret;
+}
+
static int intel_iommu_set_dirty_tracking(struct iommu_domain *domain,
bool enable)
{
@@ -4762,6 +4791,12 @@ static int intel_iommu_set_dirty_tracking(struct iommu_domain *domain,
if (ret)
goto err_unwind;
+ if (dmar_domain->nested_parent) {
+ ret = parent_domain_set_dirty_tracking(dmar_domain, enable);
+ if (ret)
+ goto err_unwind;
+ }
+
dmar_domain->dirty_tracking = enable;
out_unlock:
spin_unlock(&dmar_domain->lock);
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 09/10] iommu/vt-d: Set SSADE when attaching to a parent with dirty tracking
2024-02-19 11:15 [PATCH 00/10] [PULL REQUEST] iommu/vt-d: Fixes for v6.8-rc5 Lu Baolu
` (7 preceding siblings ...)
2024-02-19 11:15 ` [PATCH 08/10] iommu/vt-d: Add missing dirty tracking set for parent domain Lu Baolu
@ 2024-02-19 11:16 ` Lu Baolu
2024-02-19 11:16 ` [PATCH 10/10] iommu/vt-d: Fix constant-out-of-range warning Lu Baolu
2024-02-21 9:29 ` [PATCH 00/10] [PULL REQUEST] iommu/vt-d: Fixes for v6.8-rc5 Joerg Roedel
10 siblings, 0 replies; 12+ messages in thread
From: Lu Baolu @ 2024-02-19 11:16 UTC (permalink / raw)
To: Joerg Roedel
Cc: Yi Liu, Arnd Bergmann, Kevin Tian, Joao Martins, iommu,
linux-kernel
From: Yi Liu <yi.l.liu@intel.com>
Should set the SSADE (Second Stage Access/Dirty bit Enable) bit of the
pasid entry when attaching a device to a nested domain if its parent
has already enabled dirty tracking.
Fixes: 111bf85c68f6 ("iommu/vt-d: Add helper to setup pasid nested translation")
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Joao Martins <joao.m.martins@oracle.com>
Link: https://lore.kernel.org/r/20240208091414.28133-1-yi.l.liu@intel.com
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
drivers/iommu/intel/pasid.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/iommu/intel/pasid.c b/drivers/iommu/intel/pasid.c
index a32d7e509842..108158e2b907 100644
--- a/drivers/iommu/intel/pasid.c
+++ b/drivers/iommu/intel/pasid.c
@@ -657,6 +657,8 @@ int intel_pasid_setup_nested(struct intel_iommu *iommu, struct device *dev,
pasid_set_domain_id(pte, did);
pasid_set_address_width(pte, s2_domain->agaw);
pasid_set_page_snoop(pte, !!ecap_smpwc(iommu->ecap));
+ if (s2_domain->dirty_tracking)
+ pasid_set_ssade(pte);
pasid_set_translation_type(pte, PASID_ENTRY_PGTT_NESTED);
pasid_set_present(pte);
spin_unlock(&iommu->lock);
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 10/10] iommu/vt-d: Fix constant-out-of-range warning
2024-02-19 11:15 [PATCH 00/10] [PULL REQUEST] iommu/vt-d: Fixes for v6.8-rc5 Lu Baolu
` (8 preceding siblings ...)
2024-02-19 11:16 ` [PATCH 09/10] iommu/vt-d: Set SSADE when attaching to a parent with dirty tracking Lu Baolu
@ 2024-02-19 11:16 ` Lu Baolu
2024-02-21 9:29 ` [PATCH 00/10] [PULL REQUEST] iommu/vt-d: Fixes for v6.8-rc5 Joerg Roedel
10 siblings, 0 replies; 12+ messages in thread
From: Lu Baolu @ 2024-02-19 11:16 UTC (permalink / raw)
To: Joerg Roedel
Cc: Yi Liu, Arnd Bergmann, Kevin Tian, Joao Martins, iommu,
linux-kernel
From: Arnd Bergmann <arnd@arndb.de>
On 32-bit builds, the vt-d driver causes a warning with clang:
drivers/iommu/intel/nested.c:112:13: error: result of comparison of constant 18446744073709551615 with expression of type 'unsigned long' is always false [-Werror,-Wtautological-constant-out-of-range-compare]
112 | if (npages == U64_MAX)
| ~~~~~~ ^ ~~~~~~~
Make the variable a 64-bit type, which matches both the caller and the
use anyway.
Fixes: f6f3721244a8 ("iommu/vt-d: Add iotlb flush for nested domain")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Link: https://lore.kernel.org/r/20240213095832.455245-1-arnd@kernel.org
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
drivers/iommu/intel/nested.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/iommu/intel/nested.c b/drivers/iommu/intel/nested.c
index d5af5925a31c..a7d68f3d518a 100644
--- a/drivers/iommu/intel/nested.c
+++ b/drivers/iommu/intel/nested.c
@@ -103,7 +103,7 @@ static void nested_flush_dev_iotlb(struct dmar_domain *domain, u64 addr,
}
static void intel_nested_flush_cache(struct dmar_domain *domain, u64 addr,
- unsigned long npages, bool ih)
+ u64 npages, bool ih)
{
struct iommu_domain_info *info;
unsigned int mask;
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH 00/10] [PULL REQUEST] iommu/vt-d: Fixes for v6.8-rc5
2024-02-19 11:15 [PATCH 00/10] [PULL REQUEST] iommu/vt-d: Fixes for v6.8-rc5 Lu Baolu
` (9 preceding siblings ...)
2024-02-19 11:16 ` [PATCH 10/10] iommu/vt-d: Fix constant-out-of-range warning Lu Baolu
@ 2024-02-21 9:29 ` Joerg Roedel
10 siblings, 0 replies; 12+ messages in thread
From: Joerg Roedel @ 2024-02-21 9:29 UTC (permalink / raw)
To: Lu Baolu
Cc: Yi Liu, Arnd Bergmann, Kevin Tian, Joao Martins, iommu,
linux-kernel
On Mon, Feb 19, 2024 at 07:15:51PM +0800, Lu Baolu wrote:
> Yi Liu (9):
> iommu/vt-d: Track nested domains in parent
> iommu/vt-d: Add __iommu_flush_iotlb_psi()
> iommu/vt-d: Add missing iotlb flush for parent domain
> iommu/vt-d: Update iotlb in nested domain attach
> iommu/vt-d: Add missing device iotlb flush for parent domain
> iommu/vt-d: Remove domain parameter for
> intel_pasid_setup_dirty_tracking()
> iommu/vt-d: Wrap the dirty tracking loop to be a helper
> iommu/vt-d: Add missing dirty tracking set for parent domain
> iommu/vt-d: Set SSADE when attaching to a parent with dirty tracking
Applied, thanks Baolu.
^ permalink raw reply [flat|nested] 12+ messages in thread