Commit 789a5913 authored by Alejandro Jimenez's avatar Alejandro Jimenez Committed by Joerg Roedel
Browse files

iommu/amd: Use the generic iommu page table



Replace the io_pgtable versions with pt_iommu versions. The v2 page table
uses the x86 implementation that will be eventually shared with VT-d.

This supports the same special features as the original code:
 - increase_top for the v1 format to allow scaling from 3 to 6 levels
 - non-present flushing
 - Dirty tracking for v1 only
 - __sme_set() to adjust the PTEs for CC
 - Optimization for flushing with virtualization to minimize the range
 - amd_iommu_pgsize_bitmap override of the native page sizes
 - page tables allocate from the device's NUMA node

Rework the domain ops so that v1/v2 get their own ops. Make dedicated
allocation functions for v1 and v2. Hook up invalidation for a top change
to struct pt_iommu_flush_ops. Delete some of the iopgtable related code
that becomes unused in this patch. The next patch will delete the rest of
it.

This fixes a race bug in AMD's increase_address_space() implementation. It
stores the top level and top pointer in different memory, which prevents
other threads from reading a coherent version:

   increase_address_space()   alloc_pte()
                                level = pgtable->mode - 1;
	pgtable->root  = pte;
	pgtable->mode += 1;
                                pte = &pgtable->root[PM_LEVEL_INDEX(level, address)];

The iommupt version is careful to put mode and root under a single
READ_ONCE and then is careful to only READ_ONCE a single time per
walk.

Signed-off-by: default avatarAlejandro Jimenez <alejandro.j.jimenez@oracle.com>
Reviewed-by: default avatarVasant Hegde <vasant.hegde@amd.com>
Tested-by: default avatarAlejandro Jimenez <alejandro.j.jimenez@oracle.com>
Tested-by: default avatarPasha Tatashin <pasha.tatashin@soleen.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Signed-off-by: default avatarJoerg Roedel <joerg.roedel@amd.com>
parent aef5de75
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -11,10 +11,13 @@ config AMD_IOMMU
	select MMU_NOTIFIER
	select IOMMU_API
	select IOMMU_IOVA
	select IOMMU_IO_PGTABLE
	select IOMMU_SVA
	select IOMMU_IOPF
	select IOMMUFD_DRIVER if IOMMUFD
	select GENERIC_PT
	select IOMMU_PT
	select IOMMU_PT_AMDV1
	select IOMMU_PT_X86_64
	depends on X86_64 && PCI && ACPI && HAVE_CMPXCHG_DOUBLE
	help
	  With this option you can enable support for AMD IOMMU hardware in
+0 −1
Original line number Diff line number Diff line
@@ -88,7 +88,6 @@ int amd_iommu_complete_ppr(struct device *dev, u32 pasid, int status, int tag);
 * the IOMMU used by this driver.
 */
void amd_iommu_flush_all_caches(struct amd_iommu *iommu);
void amd_iommu_update_and_flush_device_table(struct protection_domain *domain);
void amd_iommu_domain_flush_pages(struct protection_domain *domain,
				  u64 address, size_t size);
void amd_iommu_dev_flush_pasid_pages(struct iommu_dev_data *dev_data,
+10 −2
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/pci.h>
#include <linux/irqreturn.h>
#include <linux/io-pgtable.h>
#include <linux/generic_pt/iommu.h>

/*
 * Maximum number of IOMMUs supported
@@ -589,9 +590,13 @@ struct pdom_iommu_info {
 * independent of their use.
 */
struct protection_domain {
	union {
		struct iommu_domain domain;
		struct pt_iommu iommu;
		struct pt_iommu_amdv1 amdv1;
		struct pt_iommu_x86_64 amdv2;
	};
	struct list_head dev_list; /* List of all devices in this domain */
	struct iommu_domain domain; /* generic domain handle used by
				       iommu core code */
	struct amd_io_pgtable iop;
	spinlock_t lock;	/* mostly used to lock the page table*/
	u16 id;			/* the domain id written to the device table */
@@ -602,6 +607,9 @@ struct protection_domain {
	struct mmu_notifier mn;	/* mmu notifier for the SVA domain */
	struct list_head dev_data_list; /* List of pdom_dev_data */
};
PT_IOMMU_CHECK_DOMAIN(struct protection_domain, iommu, domain);
PT_IOMMU_CHECK_DOMAIN(struct protection_domain, amdv1.iommu, domain);
PT_IOMMU_CHECK_DOMAIN(struct protection_domain, amdv2.iommu, domain);

/*
 * This structure contains information about one PCI segment in the system.
+0 −2
Original line number Diff line number Diff line
@@ -136,8 +136,6 @@ static bool increase_address_space(struct amd_io_pgtable *pgtable,
	pgtable->mode += 1;
	write_seqcount_end(&pgtable->seqcount);

	amd_iommu_update_and_flush_device_table(domain);

	pte = NULL;
	ret = true;

+258 −260

File changed.

Preview size limit exceeded, changes collapsed.