Skip to content

Commit 19c6978

Browse files
committed
iommu/amd: Update Device Table in increase_address_space()
The Device Table needs to be updated before the new page-table root can be published in domain->pt_root. Otherwise a concurrent call to fetch_pte might fetch a PTE which is not reachable through the Device Table Entry. Fixes: 92d420e ("iommu/amd: Relax locking in dma_ops path") Reported-by: Qian Cai <[email protected]> Signed-off-by: Joerg Roedel <[email protected]> Tested-by: Qian Cai <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Joerg Roedel <[email protected]>
1 parent f44a4d7 commit 19c6978

File tree

1 file changed

+36
-13
lines changed

1 file changed

+36
-13
lines changed

drivers/iommu/amd_iommu.c

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ struct kmem_cache *amd_iommu_irq_cache;
101101
static void update_domain(struct protection_domain *domain);
102102
static int protection_domain_init(struct protection_domain *domain);
103103
static void detach_device(struct device *dev);
104+
static void update_and_flush_device_table(struct protection_domain *domain,
105+
struct domain_pgtable *pgtable);
104106

105107
/****************************************************************************
106108
*
@@ -1461,8 +1463,16 @@ static bool increase_address_space(struct protection_domain *domain,
14611463

14621464
*pte = PM_LEVEL_PDE(pgtable.mode, iommu_virt_to_phys(pgtable.root));
14631465

1464-
root = amd_iommu_domain_encode_pgtable(pte, pgtable.mode + 1);
1466+
pgtable.root = pte;
1467+
pgtable.mode += 1;
1468+
update_and_flush_device_table(domain, &pgtable);
1469+
domain_flush_complete(domain);
14651470

1471+
/*
1472+
* Device Table needs to be updated and flushed before the new root can
1473+
* be published.
1474+
*/
1475+
root = amd_iommu_domain_encode_pgtable(pte, pgtable.mode);
14661476
atomic64_set(&domain->pt_root, root);
14671477

14681478
ret = true;
@@ -1893,19 +1903,17 @@ static bool dma_ops_domain(struct protection_domain *domain)
18931903
}
18941904

18951905
static void set_dte_entry(u16 devid, struct protection_domain *domain,
1906+
struct domain_pgtable *pgtable,
18961907
bool ats, bool ppr)
18971908
{
1898-
struct domain_pgtable pgtable;
18991909
u64 pte_root = 0;
19001910
u64 flags = 0;
19011911
u32 old_domid;
19021912

1903-
amd_iommu_domain_get_pgtable(domain, &pgtable);
1913+
if (pgtable->mode != PAGE_MODE_NONE)
1914+
pte_root = iommu_virt_to_phys(pgtable->root);
19041915

1905-
if (pgtable.mode != PAGE_MODE_NONE)
1906-
pte_root = iommu_virt_to_phys(pgtable.root);
1907-
1908-
pte_root |= (pgtable.mode & DEV_ENTRY_MODE_MASK)
1916+
pte_root |= (pgtable->mode & DEV_ENTRY_MODE_MASK)
19091917
<< DEV_ENTRY_MODE_SHIFT;
19101918
pte_root |= DTE_FLAG_IR | DTE_FLAG_IW | DTE_FLAG_V | DTE_FLAG_TV;
19111919

@@ -1978,6 +1986,7 @@ static void clear_dte_entry(u16 devid)
19781986
static void do_attach(struct iommu_dev_data *dev_data,
19791987
struct protection_domain *domain)
19801988
{
1989+
struct domain_pgtable pgtable;
19811990
struct amd_iommu *iommu;
19821991
bool ats;
19831992

@@ -1993,7 +2002,9 @@ static void do_attach(struct iommu_dev_data *dev_data,
19932002
domain->dev_cnt += 1;
19942003

19952004
/* Update device table */
1996-
set_dte_entry(dev_data->devid, domain, ats, dev_data->iommu_v2);
2005+
amd_iommu_domain_get_pgtable(domain, &pgtable);
2006+
set_dte_entry(dev_data->devid, domain, &pgtable,
2007+
ats, dev_data->iommu_v2);
19972008
clone_aliases(dev_data->pdev);
19982009

19992010
device_flush_dte(dev_data);
@@ -2304,22 +2315,34 @@ static int amd_iommu_domain_get_attr(struct iommu_domain *domain,
23042315
*
23052316
*****************************************************************************/
23062317

2307-
static void update_device_table(struct protection_domain *domain)
2318+
static void update_device_table(struct protection_domain *domain,
2319+
struct domain_pgtable *pgtable)
23082320
{
23092321
struct iommu_dev_data *dev_data;
23102322

23112323
list_for_each_entry(dev_data, &domain->dev_list, list) {
2312-
set_dte_entry(dev_data->devid, domain, dev_data->ats.enabled,
2313-
dev_data->iommu_v2);
2324+
set_dte_entry(dev_data->devid, domain, pgtable,
2325+
dev_data->ats.enabled, dev_data->iommu_v2);
23142326
clone_aliases(dev_data->pdev);
23152327
}
23162328
}
23172329

2330+
static void update_and_flush_device_table(struct protection_domain *domain,
2331+
struct domain_pgtable *pgtable)
2332+
{
2333+
update_device_table(domain, pgtable);
2334+
domain_flush_devices(domain);
2335+
}
2336+
23182337
static void update_domain(struct protection_domain *domain)
23192338
{
2320-
update_device_table(domain);
2339+
struct domain_pgtable pgtable;
23212340

2322-
domain_flush_devices(domain);
2341+
/* Update device table */
2342+
amd_iommu_domain_get_pgtable(domain, &pgtable);
2343+
update_and_flush_device_table(domain, &pgtable);
2344+
2345+
/* Flush domain TLB(s) and wait for completion */
23232346
domain_flush_tlb_pde(domain);
23242347
domain_flush_complete(domain);
23252348
}

0 commit comments

Comments
 (0)