From: Vasant Hegde <vasant.hegde@amd.com>
To: <iommu@lists.linux.dev>, <joro@8bytes.org>
Cc: <suravee.suthikulpanit@amd.com>, <wei.huang2@amd.com>,
<jsnitsel@redhat.com>, <jgg@ziepe.ca>,
Vasant Hegde <vasant.hegde@amd.com>
Subject: [PATCH v5 07/17] iommu/amd: Introduce per-device domain ID to workaround potential TLB aliasing issue
Date: Tue, 16 Jan 2024 16:53:25 +0000 [thread overview]
Message-ID: <20240116165335.6043-8-vasant.hegde@amd.com> (raw)
In-Reply-To: <20240116165335.6043-1-vasant.hegde@amd.com>
With v1 page table, the AMD IOMMU spec states that the hardware must use
the domain ID to tag its internal translation caches. I/O devices with
different v1 page tables must be given different domain IDs. I/O devices
that share the same v1 page table __may__ be given the same domain ID.
This domain ID management policy is currently implemented by the AMD
IOMMU driver. In this case, only the domain ID is needed when issuing the
INVALIDATE_IOMMU_PAGES command to invalidate the IOMMU translation cache
(TLB).
With v2 page table, the hardware uses domain ID and PASID as parameters
to tag and issue the INVALIDATE_IOMMU_PAGES command. Since the GCR3 table
is setup per-device, and there is no guarantee for PASID to be unique
across multiple devices. The same PASID for different devices could
have different v2 page tables. In such case, if multiple devices share the
same domain ID, IOMMU translation cache for these devices would be polluted
due to TLB aliasing.
Hence, avoid the TLB aliasing issue with v2 page table by allocating unique
domain ID for each device even when multiple devices are sharing the same v1
page table. Please note that this workaround would result in multiple
INVALIDATE_IOMMU_PAGES commands (one per domain id) when unmapping a
translation.
Domain ID can be shared until device starts using PASID. We will enhance this
code later where we will allocate per device domain ID only when its needed.
Signed-off-by: Vasant Hegde <vasant.hegde@amd.com>
---
drivers/iommu/amd/amd_iommu_types.h | 1 +
drivers/iommu/amd/iommu.c | 84 +++++++++++++++++++++++------
2 files changed, 70 insertions(+), 15 deletions(-)
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index fead9033796f..9e23710fd3ec 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -545,6 +545,7 @@ struct gcr3_tbl_info {
u64 *gcr3_tbl; /* Guest CR3 table */
int glx; /* Number of levels for GCR3 table */
u32 pasid_cnt; /* Track attached PASIDs */
+ u16 domid; /* Per device domain ID */
};
struct amd_io_pgtable {
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index dac04585c7fc..34004f0e90dc 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -90,6 +90,14 @@ static inline bool pdom_is_v2_pgtbl_mode(struct protection_domain *pdom)
return (pdom && (pdom->flags & PD_IOMMUV2_MASK));
}
+/*
+ * Allocate per device domain ID when using V2 page table
+ */
+static inline bool domain_id_is_per_dev(struct protection_domain *pdom)
+{
+ return (pdom && pdom->pd_mode != PD_MODE_V1);
+}
+
static inline int get_acpihid_device_id(struct device *dev,
struct acpihid_map_entry **entry)
{
@@ -1444,27 +1452,37 @@ static int device_flush_dte(struct iommu_dev_data *dev_data)
return ret;
}
-/*
- * TLB invalidation function which is called from the mapping functions.
- * It invalidates a single PTE if the range to flush is within a single
- * page. Otherwise it flushes the whole TLB of the IOMMU.
- */
-static void __domain_flush_pages(struct protection_domain *domain,
+static int domain_flush_pages_v2(struct protection_domain *pdom,
u64 address, size_t size)
{
struct iommu_dev_data *dev_data;
struct iommu_cmd cmd;
- int ret = 0, i;
- ioasid_t pasid = IOMMU_NO_PASID;
- bool gn = false;
+ int ret = 0;
- if (pdom_is_v2_pgtbl_mode(domain))
- gn = true;
+ list_for_each_entry(dev_data, &pdom->dev_list, list) {
+ struct amd_iommu *iommu = get_amd_iommu_from_dev(dev_data->dev);
+ u16 domid = dev_data->gcr3_info.domid;
+
+ build_inv_iommu_pages(&cmd, address, size,
+ domid, IOMMU_NO_PASID, true);
+
+ ret |= iommu_queue_command(iommu, &cmd);
+ }
+
+ return ret;
+}
- build_inv_iommu_pages(&cmd, address, size, domain->id, pasid, gn);
+static int domain_flush_pages_v1(struct protection_domain *pdom,
+ u64 address, size_t size)
+{
+ struct iommu_cmd cmd;
+ int ret = 0, i;
+
+ build_inv_iommu_pages(&cmd, address, size,
+ pdom->id, IOMMU_NO_PASID, false);
for (i = 0; i < amd_iommu_get_num_iommus(); ++i) {
- if (!domain->dev_iommu[i])
+ if (!pdom->dev_iommu[i])
continue;
/*
@@ -1474,6 +1492,28 @@ static void __domain_flush_pages(struct protection_domain *domain,
ret |= iommu_queue_command(amd_iommus[i], &cmd);
}
+ return ret;
+}
+
+/*
+ * TLB invalidation function which is called from the mapping functions.
+ * It flushes range of PTEs of the domain.
+ */
+static void __domain_flush_pages(struct protection_domain *domain,
+ u64 address, size_t size)
+{
+ struct iommu_dev_data *dev_data;
+ int ret = 0;
+ ioasid_t pasid = IOMMU_NO_PASID;
+ bool gn = false;
+
+ if (pdom_is_v2_pgtbl_mode(domain)) {
+ gn = true;
+ ret = domain_flush_pages_v2(domain, address, size);
+ } else {
+ ret = domain_flush_pages_v1(domain, address, size);
+ }
+
list_for_each_entry(dev_data, &domain->dev_list, list) {
if (!dev_data->ats_enabled)
@@ -1702,9 +1742,15 @@ static void set_dte_entry(struct amd_iommu *iommu,
u64 flags = 0;
u32 old_domid;
u16 devid = dev_data->devid;
+ u16 domid;
struct protection_domain *domain = dev_data->domain;
struct dev_table_entry *dev_table = get_dev_table(iommu);
+ if (domain_id_is_per_dev(domain))
+ domid = dev_data->gcr3_info.domid;
+ else
+ domid = domain->id;
+
if (domain->iop.mode != PAGE_MODE_NONE)
pte_root = iommu_virt_to_phys(domain->iop.root);
@@ -1717,7 +1763,7 @@ static void set_dte_entry(struct amd_iommu *iommu,
* When SNP is enabled, Only set TV bit when IOMMU
* page translation is in use.
*/
- if (!amd_iommu_snp_en || (domain->id != 0))
+ if (!amd_iommu_snp_en || (domid != 0))
pte_root |= DTE_FLAG_TV;
flags = dev_table[devid].data[1];
@@ -1766,7 +1812,7 @@ static void set_dte_entry(struct amd_iommu *iommu,
}
flags &= ~DEV_DOMID_MASK;
- flags |= domain->id;
+ flags |= domid;
old_domid = dev_table[devid].data[1] & DEV_DOMID_MASK;
dev_table[devid].data[1] = flags;
@@ -1814,6 +1860,10 @@ static void do_attach(struct iommu_dev_data *dev_data,
domain->dev_iommu[iommu->index] += 1;
domain->dev_cnt += 1;
+ /* Allocate per device domain ID */
+ if (domain_id_is_per_dev(domain))
+ dev_data->gcr3_info.domid = domain_id_alloc();
+
/* Update device table */
set_dte_entry(iommu, dev_data);
clone_aliases(iommu, dev_data->dev);
@@ -1841,6 +1891,10 @@ static void do_detach(struct iommu_dev_data *dev_data)
/* decrease reference counters - needs to happen after the flushes */
domain->dev_iommu[iommu->index] -= 1;
domain->dev_cnt -= 1;
+
+ /* Free per device domain ID */
+ if (domain_id_is_per_dev(domain))
+ domain_id_free(dev_data->gcr3_info.domid);
}
/*
--
2.31.1
next prev parent reply other threads:[~2024-01-16 16:55 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-01-16 16:53 [PATCH v5 00/17] iommu/amd: SVA Support (part 3) - refactor support for GCR3 table Vasant Hegde
2024-01-16 16:53 ` [PATCH v5 01/17] iommu/amd: Pass struct iommu_dev_data to set_dte_entry() Vasant Hegde
2024-01-16 16:53 ` [PATCH v5 02/17] iommu/amd: Enable Guest Translation before registering devices Vasant Hegde
2024-01-16 16:53 ` [PATCH v5 03/17] iommu/amd: Introduce get_amd_iommu_from_dev() Vasant Hegde
2024-01-19 19:00 ` Jason Gunthorpe
2024-01-16 16:53 ` [PATCH v5 04/17] iommu/amd: Introduce struct protection_domain.pd_mode Vasant Hegde
2024-01-16 16:53 ` [PATCH v5 05/17] iommu/amd: Introduce per-device GCR3 table Vasant Hegde
2024-01-19 19:03 ` Jason Gunthorpe
2024-01-16 16:53 ` [PATCH v5 06/17] iommu/amd: Use protection_domain.flags to check page table mode Vasant Hegde
2024-01-16 16:53 ` Vasant Hegde [this message]
2024-01-16 16:53 ` [PATCH v5 08/17] iommu/amd: Add support for device based TLB invalidation Vasant Hegde
2024-01-16 16:53 ` [PATCH v5 09/17] iommu/amd: Rearrange GCR3 table setup code Vasant Hegde
2024-01-16 16:53 ` [PATCH v5 10/17] iommu: Introduce iommu_group_mutex_assert() Vasant Hegde
2024-01-19 19:09 ` Jason Gunthorpe
2024-01-22 6:24 ` Vasant Hegde
2024-01-22 18:00 ` Jason Gunthorpe
2024-01-23 5:27 ` Vasant Hegde
2024-01-16 16:53 ` [PATCH v5 11/17] iommu/amd: Refactor helper function for setting / clearing GCR3 Vasant Hegde
2024-01-16 16:53 ` [PATCH v5 12/17] iommu/amd: Refactor attaching / detaching device functions Vasant Hegde
2024-01-16 16:53 ` [PATCH v5 13/17] iommu/amd: Refactor protection_domain helper functions Vasant Hegde
2024-01-16 16:53 ` [PATCH v5 14/17] iommu/amd: Refactor GCR3 table " Vasant Hegde
2024-01-19 19:59 ` Jason Gunthorpe
2024-01-22 10:23 ` Vasant Hegde
2024-01-22 18:26 ` Jason Gunthorpe
2024-01-23 8:54 ` Vasant Hegde
2024-01-25 1:46 ` Jason Gunthorpe
2024-01-25 12:11 ` Vasant Hegde
2024-01-25 15:53 ` Jason Gunthorpe
2024-01-16 16:53 ` [PATCH v5 15/17] iommu/amd: Remove unused flush pasid functions Vasant Hegde
2024-01-16 16:53 ` [PATCH v5 16/17] iommu/amd: Rearrange device flush code Vasant Hegde
2024-01-16 16:53 ` [PATCH v5 17/17] iommu/amd: Remove unused GCR3 table parameters from struct protection_domain Vasant Hegde
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240116165335.6043-8-vasant.hegde@amd.com \
--to=vasant.hegde@amd.com \
--cc=iommu@lists.linux.dev \
--cc=jgg@ziepe.ca \
--cc=joro@8bytes.org \
--cc=jsnitsel@redhat.com \
--cc=suravee.suthikulpanit@amd.com \
--cc=wei.huang2@amd.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox