Skip to content

Commit a270be1

Browse files
anadavjoergroedel
authored andcommitted
iommu/amd: Use only natural aligned flushes in a VM
When running on an AMD vIOMMU, it is better to avoid TLB flushes of unmodified PTEs. vIOMMUs require the hypervisor to synchronize the virtualized IOMMU's PTEs with the physical ones. This process induce overheads. AMD IOMMU allows us to flush any range that is aligned to the power of 2. So when running on top of a vIOMMU, break the range into sub-ranges that are naturally aligned, and flush each one separately. This apporach is better when running with a vIOMMU, but on physical IOMMUs, the penalty of IOTLB misses due to unnecessary flushed entries is likely to be low. Repurpose (i.e., keeping the name, changing the logic) domain_flush_pages() so it is used to choose whether to perform one flush of the whole range or multiple ones to avoid flushing unnecessary ranges. Use NpCache, as usual, to infer whether the IOMMU is physical or virtual. Cc: Joerg Roedel <[email protected]> Cc: Will Deacon <[email protected]> Cc: Jiajun Cao <[email protected]> Cc: Lu Baolu <[email protected]> Cc: [email protected] Cc: [email protected] Suggested-by: Robin Murphy <[email protected]> Signed-off-by: Nadav Amit <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Joerg Roedel <[email protected]>
1 parent 3b122a5 commit a270be1

File tree

1 file changed

+42
-5
lines changed

1 file changed

+42
-5
lines changed

drivers/iommu/amd/iommu.c

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,15 +1267,52 @@ static void __domain_flush_pages(struct protection_domain *domain,
12671267
}
12681268

12691269
static void domain_flush_pages(struct protection_domain *domain,
1270-
u64 address, size_t size)
1270+
u64 address, size_t size, int pde)
12711271
{
1272-
__domain_flush_pages(domain, address, size, 0);
1272+
if (likely(!amd_iommu_np_cache)) {
1273+
__domain_flush_pages(domain, address, size, pde);
1274+
return;
1275+
}
1276+
1277+
/*
1278+
* When NpCache is on, we infer that we run in a VM and use a vIOMMU.
1279+
* In such setups it is best to avoid flushes of ranges which are not
1280+
* naturally aligned, since it would lead to flushes of unmodified
1281+
* PTEs. Such flushes would require the hypervisor to do more work than
1282+
* necessary. Therefore, perform repeated flushes of aligned ranges
1283+
* until you cover the range. Each iteration flushes the smaller
1284+
* between the natural alignment of the address that we flush and the
1285+
* greatest naturally aligned region that fits in the range.
1286+
*/
1287+
while (size != 0) {
1288+
int addr_alignment = __ffs(address);
1289+
int size_alignment = __fls(size);
1290+
int min_alignment;
1291+
size_t flush_size;
1292+
1293+
/*
1294+
* size is always non-zero, but address might be zero, causing
1295+
* addr_alignment to be negative. As the casting of the
1296+
* argument in __ffs(address) to long might trim the high bits
1297+
* of the address on x86-32, cast to long when doing the check.
1298+
*/
1299+
if (likely((unsigned long)address != 0))
1300+
min_alignment = min(addr_alignment, size_alignment);
1301+
else
1302+
min_alignment = size_alignment;
1303+
1304+
flush_size = 1ul << min_alignment;
1305+
1306+
__domain_flush_pages(domain, address, flush_size, pde);
1307+
address += flush_size;
1308+
size -= flush_size;
1309+
}
12731310
}
12741311

12751312
/* Flush the whole IO/TLB for a given protection domain - including PDE */
12761313
void amd_iommu_domain_flush_tlb_pde(struct protection_domain *domain)
12771314
{
1278-
__domain_flush_pages(domain, 0, CMD_INV_IOMMU_ALL_PAGES_ADDRESS, 1);
1315+
domain_flush_pages(domain, 0, CMD_INV_IOMMU_ALL_PAGES_ADDRESS, 1);
12791316
}
12801317

12811318
void amd_iommu_domain_flush_complete(struct protection_domain *domain)
@@ -1302,7 +1339,7 @@ static void domain_flush_np_cache(struct protection_domain *domain,
13021339
unsigned long flags;
13031340

13041341
spin_lock_irqsave(&domain->lock, flags);
1305-
domain_flush_pages(domain, iova, size);
1342+
domain_flush_pages(domain, iova, size, 1);
13061343
amd_iommu_domain_flush_complete(domain);
13071344
spin_unlock_irqrestore(&domain->lock, flags);
13081345
}
@@ -2206,7 +2243,7 @@ static void amd_iommu_iotlb_sync(struct iommu_domain *domain,
22062243
unsigned long flags;
22072244

22082245
spin_lock_irqsave(&dom->lock, flags);
2209-
__domain_flush_pages(dom, gather->start, gather->end - gather->start, 1);
2246+
domain_flush_pages(dom, gather->start, gather->end - gather->start, 1);
22102247
amd_iommu_domain_flush_complete(dom);
22112248
spin_unlock_irqrestore(&dom->lock, flags);
22122249
}

0 commit comments

Comments
 (0)