Skip to content

Commit

Permalink
KVM: PPC: Optimize clearing TCEs for sparse tables
Browse files Browse the repository at this point in the history
The powernv platform maintains 2 TCE tables for VFIO - a hardware TCE
table and a table with userspace addresses. These tables are radix trees,
we allocate indirect levels when they are written to. Since
the memory allocation is problematic in real mode, we have 2 accessors
to the entries:
- for virtual mode: it allocates the memory and it is always expected
to return non-NULL;
- fr real mode: it does not allocate and can return NULL.

Also, DMA windows can span to up to 55 bits of the address space and since
we never have this much RAM, such windows are sparse. However currently
the SPAPR TCE IOMMU driver walks through all TCEs to unpin DMA memory.

Since we maintain a userspace addresses table for VFIO which is a mirror
of the hardware table, we can use it to know which parts of the DMA
window have not been mapped and skip these so does this patch.

The bare metal systems do not have this problem as they use a bypass mode
of a PHB which maps RAM directly.

This helps a lot with sparse DMA windows, reducing the shutdown time from
about 3 minutes per 1 billion TCEs to a few seconds for 32GB sparse guest.
Just skipping the last level seems to be good enough.

As non-allocating accessor is used now in virtual mode as well, rename it
from IOMMU_TABLE_USERSPACE_ENTRY_RM (real mode) to _RO (read only).

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
  • Loading branch information
Alexey Kardashevskiy authored and Paul Mackerras committed Oct 20, 2018
1 parent 8d9fcac commit 6e301a8
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 9 deletions.
2 changes: 1 addition & 1 deletion arch/powerpc/include/asm/iommu.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ struct iommu_table {
int it_nid;
};

#define IOMMU_TABLE_USERSPACE_ENTRY_RM(tbl, entry) \
#define IOMMU_TABLE_USERSPACE_ENTRY_RO(tbl, entry) \
((tbl)->it_ops->useraddrptr((tbl), (entry), false))
#define IOMMU_TABLE_USERSPACE_ENTRY(tbl, entry) \
((tbl)->it_ops->useraddrptr((tbl), (entry), true))
Expand Down
5 changes: 2 additions & 3 deletions arch/powerpc/kvm/book3s_64_vio.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,11 +410,10 @@ static long kvmppc_tce_iommu_mapped_dec(struct kvm *kvm,
{
struct mm_iommu_table_group_mem_t *mem = NULL;
const unsigned long pgsize = 1ULL << tbl->it_page_shift;
__be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY(tbl, entry);
__be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY_RO(tbl, entry);

if (!pua)
/* it_userspace allocation might be delayed */
return H_TOO_HARD;
return H_SUCCESS;

mem = mm_iommu_lookup(kvm->mm, be64_to_cpu(*pua), pgsize);
if (!mem)
Expand Down
6 changes: 3 additions & 3 deletions arch/powerpc/kvm/book3s_64_vio_hv.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ static long iommu_tce_xchg_rm(struct mm_struct *mm, struct iommu_table *tbl,

if (!ret && ((*direction == DMA_FROM_DEVICE) ||
(*direction == DMA_BIDIRECTIONAL))) {
__be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY_RM(tbl, entry);
__be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY_RO(tbl, entry);
/*
* kvmppc_rm_tce_iommu_do_map() updates the UA cache after
* calling this so we still get here a valid UA.
Expand All @@ -240,7 +240,7 @@ static long kvmppc_rm_tce_iommu_mapped_dec(struct kvm *kvm,
{
struct mm_iommu_table_group_mem_t *mem = NULL;
const unsigned long pgsize = 1ULL << tbl->it_page_shift;
__be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY_RM(tbl, entry);
__be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY_RO(tbl, entry);

if (!pua)
/* it_userspace allocation might be delayed */
Expand Down Expand Up @@ -304,7 +304,7 @@ static long kvmppc_rm_tce_iommu_do_map(struct kvm *kvm, struct iommu_table *tbl,
{
long ret;
unsigned long hpa = 0;
__be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY_RM(tbl, entry);
__be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY_RO(tbl, entry);
struct mm_iommu_table_group_mem_t *mem;

if (!pua)
Expand Down
23 changes: 21 additions & 2 deletions drivers/vfio/vfio_iommu_spapr_tce.c
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ static void tce_iommu_unuse_page_v2(struct tce_container *container,
struct mm_iommu_table_group_mem_t *mem = NULL;
int ret;
unsigned long hpa = 0;
__be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY(tbl, entry);
__be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY_RO(tbl, entry);

if (!pua)
return;
Expand All @@ -467,8 +467,27 @@ static int tce_iommu_clear(struct tce_container *container,
unsigned long oldhpa;
long ret;
enum dma_data_direction direction;
unsigned long lastentry = entry + pages;

for ( ; entry < lastentry; ++entry) {
if (tbl->it_indirect_levels && tbl->it_userspace) {
/*
* For multilevel tables, we can take a shortcut here
* and skip some TCEs as we know that the userspace
* addresses cache is a mirror of the real TCE table
* and if it is missing some indirect levels, then
* the hardware table does not have them allocated
* either and therefore does not require updating.
*/
__be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY_RO(tbl,
entry);
if (!pua) {
/* align to level_size which is power of two */
entry |= tbl->it_level_size - 1;
continue;
}
}

for ( ; pages; --pages, ++entry) {
cond_resched();

direction = DMA_NONE;
Expand Down

0 comments on commit 6e301a8

Please sign in to comment.