Skip to content

Commit 87a6f1f

Browse files
hegdevasantjoergroedel
authored andcommitted
iommu/amd: Introduce per-device domain ID to fix potential TLB aliasing issue
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 fix 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 <[email protected]> Reviewed-by: Jason Gunthorpe <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Joerg Roedel <[email protected]>
1 parent c2a6af5 commit 87a6f1f

File tree

2 files changed

+64
-17
lines changed

2 files changed

+64
-17
lines changed

drivers/iommu/amd/amd_iommu_types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ struct gcr3_tbl_info {
528528
u64 *gcr3_tbl; /* Guest CR3 table */
529529
int glx; /* Number of levels for GCR3 table */
530530
u32 pasid_cnt; /* Track attached PASIDs */
531+
u16 domid; /* Per device domain ID */
531532
};
532533

533534
struct amd_io_pgtable {

drivers/iommu/amd/iommu.c

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1443,27 +1443,37 @@ static int device_flush_dte(struct iommu_dev_data *dev_data)
14431443
return ret;
14441444
}
14451445

1446-
/*
1447-
* TLB invalidation function which is called from the mapping functions.
1448-
* It invalidates a single PTE if the range to flush is within a single
1449-
* page. Otherwise it flushes the whole TLB of the IOMMU.
1450-
*/
1451-
static void __domain_flush_pages(struct protection_domain *domain,
1446+
static int domain_flush_pages_v2(struct protection_domain *pdom,
14521447
u64 address, size_t size)
14531448
{
14541449
struct iommu_dev_data *dev_data;
14551450
struct iommu_cmd cmd;
1456-
int ret = 0, i;
1457-
ioasid_t pasid = IOMMU_NO_PASID;
1458-
bool gn = false;
1451+
int ret = 0;
14591452

1460-
if (pdom_is_v2_pgtbl_mode(domain))
1461-
gn = true;
1453+
list_for_each_entry(dev_data, &pdom->dev_list, list) {
1454+
struct amd_iommu *iommu = get_amd_iommu_from_dev(dev_data->dev);
1455+
u16 domid = dev_data->gcr3_info.domid;
1456+
1457+
build_inv_iommu_pages(&cmd, address, size,
1458+
domid, IOMMU_NO_PASID, true);
1459+
1460+
ret |= iommu_queue_command(iommu, &cmd);
1461+
}
14621462

1463-
build_inv_iommu_pages(&cmd, address, size, domain->id, pasid, gn);
1463+
return ret;
1464+
}
1465+
1466+
static int domain_flush_pages_v1(struct protection_domain *pdom,
1467+
u64 address, size_t size)
1468+
{
1469+
struct iommu_cmd cmd;
1470+
int ret = 0, i;
1471+
1472+
build_inv_iommu_pages(&cmd, address, size,
1473+
pdom->id, IOMMU_NO_PASID, false);
14641474

14651475
for (i = 0; i < amd_iommu_get_num_iommus(); ++i) {
1466-
if (!domain->dev_iommu[i])
1476+
if (!pdom->dev_iommu[i])
14671477
continue;
14681478

14691479
/*
@@ -1473,6 +1483,28 @@ static void __domain_flush_pages(struct protection_domain *domain,
14731483
ret |= iommu_queue_command(amd_iommus[i], &cmd);
14741484
}
14751485

1486+
return ret;
1487+
}
1488+
1489+
/*
1490+
* TLB invalidation function which is called from the mapping functions.
1491+
* It flushes range of PTEs of the domain.
1492+
*/
1493+
static void __domain_flush_pages(struct protection_domain *domain,
1494+
u64 address, size_t size)
1495+
{
1496+
struct iommu_dev_data *dev_data;
1497+
int ret = 0;
1498+
ioasid_t pasid = IOMMU_NO_PASID;
1499+
bool gn = false;
1500+
1501+
if (pdom_is_v2_pgtbl_mode(domain)) {
1502+
gn = true;
1503+
ret = domain_flush_pages_v2(domain, address, size);
1504+
} else {
1505+
ret = domain_flush_pages_v1(domain, address, size);
1506+
}
1507+
14761508
list_for_each_entry(dev_data, &domain->dev_list, list) {
14771509

14781510
if (!dev_data->ats_enabled)
@@ -1548,7 +1580,7 @@ void amd_iommu_dev_flush_pasid_pages(struct iommu_dev_data *dev_data,
15481580
struct amd_iommu *iommu = get_amd_iommu_from_dev(dev_data->dev);
15491581

15501582
build_inv_iommu_pages(&cmd, address, size,
1551-
dev_data->domain->id, pasid, true);
1583+
dev_data->gcr3_info.domid, pasid, true);
15521584
iommu_queue_command(iommu, &cmd);
15531585

15541586
if (dev_data->ats_enabled)
@@ -1723,6 +1755,9 @@ static void free_gcr3_table(struct gcr3_tbl_info *gcr3_info)
17231755

17241756
gcr3_info->glx = 0;
17251757

1758+
/* Free per device domain ID */
1759+
domain_id_free(gcr3_info->domid);
1760+
17261761
free_page((unsigned long)gcr3_info->gcr3_tbl);
17271762
gcr3_info->gcr3_tbl = NULL;
17281763
}
@@ -1755,9 +1790,14 @@ static int setup_gcr3_table(struct gcr3_tbl_info *gcr3_info,
17551790
if (gcr3_info->gcr3_tbl)
17561791
return -EBUSY;
17571792

1793+
/* Allocate per device domain ID */
1794+
gcr3_info->domid = domain_id_alloc();
1795+
17581796
gcr3_info->gcr3_tbl = alloc_pgtable_page(nid, GFP_KERNEL);
1759-
if (gcr3_info->gcr3_tbl == NULL)
1797+
if (gcr3_info->gcr3_tbl == NULL) {
1798+
domain_id_free(gcr3_info->domid);
17601799
return -ENOMEM;
1800+
}
17611801

17621802
gcr3_info->glx = levels;
17631803

@@ -1856,10 +1896,16 @@ static void set_dte_entry(struct amd_iommu *iommu,
18561896
u64 flags = 0;
18571897
u32 old_domid;
18581898
u16 devid = dev_data->devid;
1899+
u16 domid;
18591900
struct protection_domain *domain = dev_data->domain;
18601901
struct dev_table_entry *dev_table = get_dev_table(iommu);
18611902
struct gcr3_tbl_info *gcr3_info = &dev_data->gcr3_info;
18621903

1904+
if (gcr3_info && gcr3_info->gcr3_tbl)
1905+
domid = dev_data->gcr3_info.domid;
1906+
else
1907+
domid = domain->id;
1908+
18631909
if (domain->iop.mode != PAGE_MODE_NONE)
18641910
pte_root = iommu_virt_to_phys(domain->iop.root);
18651911

@@ -1872,7 +1918,7 @@ static void set_dte_entry(struct amd_iommu *iommu,
18721918
* When SNP is enabled, Only set TV bit when IOMMU
18731919
* page translation is in use.
18741920
*/
1875-
if (!amd_iommu_snp_en || (domain->id != 0))
1921+
if (!amd_iommu_snp_en || (domid != 0))
18761922
pte_root |= DTE_FLAG_TV;
18771923

18781924
flags = dev_table[devid].data[1];
@@ -1922,7 +1968,7 @@ static void set_dte_entry(struct amd_iommu *iommu,
19221968
}
19231969

19241970
flags &= ~DEV_DOMID_MASK;
1925-
flags |= domain->id;
1971+
flags |= domid;
19261972

19271973
old_domid = dev_table[devid].data[1] & DEV_DOMID_MASK;
19281974
dev_table[devid].data[1] = flags;

0 commit comments

Comments
 (0)