IOMMU Fixes for Linux v6.17-rc6

Including:
 
 	- Fixes for memory leak and memory corruption bugs on S390 and AMD-Vi.
 
 	- Race condition fix in AMD-Vi page table code and S390 device attach
 	  code.
 
 	- Intel VT-d: Fix alignment checks in __domain_mapping().
 
 	- AMD-Vi: Fix potentially incorrect DTE settings when device has
 	  aliases.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEr9jSbILcajRFYWYyK/BELZcBGuMFAmjNaFkACgkQK/BELZcB
 GuPEZw//U0Y64dlIPfWwVp1EgZqeaLlSVChtdvtuOMECZFeziqsEUV+nHlknONyi
 jIJbDkS8eV80TOi83Wk5Z/42W05v/tFC/oD8vkyoT4jGCF7m2oyTuIFj1gBhXvUU
 9ZYGCS+CcnGw5WRJau3UeKT8CkUw5z4VCUotYPQpN/S+FK6WwMeUCBz3d/vLIoBC
 7OMC2XMBcMWYtqwansHWEfPnywEeTy5M/NW8FHEQd/wrxMSW0n3xHoia+6LQHAPF
 Dr347xeAaYQ3wqEtlbPFPXDYrmiIPq8DiTZslCvYfpdYY15DPA00r/FRq9Q5dmFL
 eQuLdcW86DDgbjyfZmRReYZv2o6vyMfOFBS5o2wrfBzOrqDiUwbyV/V/yxs30Gc1
 ZNWuKzzI8eOcv+9kkmxafShMhMJx1hJ08sMVUYgFXl3UUe4oQe7o6qRbaHDwcTjM
 +eMfP4Y4YiQtIMdosCKrnFiryb6IXu91f6WwiBZiiiW2JO8XuW8idJ9RMToNfkPI
 HyZRlRO5dO3cedu0lr8xWaS3/g9QDltXTJFpbF5UDX6XeTvn7D5CGQsSglNQ4spM
 1BOaH7KdALaq7irOSxXwXuD+XsvwDm3uhFvx0cK3LMYzJFiy+h8dqlL6yzEu5clM
 LihyHkhCQJgDECVoykNKAX0J4HMZmY5+YRfEOLxuaBN7yUwVxgc=
 =hreJ
 -----END PGP SIGNATURE-----

Merge tag 'iommu-fixes-v6.17-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux

Pull iommu fixes from Joerg Roedel:

 - Fixes for memory leak and memory corruption bugs on S390 and AMD-Vi

 - Race condition fix in AMD-Vi page table code and S390 device attach
   code

 - Intel VT-d: Fix alignment checks in __domain_mapping()

 - AMD-Vi: Fix potentially incorrect DTE settings when device has
   aliases

* tag 'iommu-fixes-v6.17-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux:
  iommu/amd/pgtbl: Fix possible race while increase page table level
  iommu/amd: Fix alias device DTE setting
  iommu/s390: Make attach succeed when the device was surprise removed
  iommu/vt-d: Fix __domain_mapping()'s usage of switch_to_super_page()
  iommu/s390: Fix memory corruption when using identity domain
  iommu/amd: Fix ivrs_base memleak in early_amd_iommu_init()
This commit is contained in:
Linus Torvalds 2025-09-19 16:02:48 -07:00
commit 497b9a7b8d
6 changed files with 59 additions and 22 deletions

View File

@ -16,11 +16,11 @@
#define ZPCI_PCI_ST_FUNC_NOT_AVAIL 40
#define ZPCI_PCI_ST_ALREADY_IN_RQ_STATE 44
/* Load/Store return codes */
#define ZPCI_PCI_LS_OK 0
#define ZPCI_PCI_LS_ERR 1
#define ZPCI_PCI_LS_BUSY 2
#define ZPCI_PCI_LS_INVAL_HANDLE 3
/* PCI instruction condition codes */
#define ZPCI_CC_OK 0
#define ZPCI_CC_ERR 1
#define ZPCI_CC_BUSY 2
#define ZPCI_CC_INVAL_HANDLE 3
/* Load/Store address space identifiers */
#define ZPCI_PCIAS_MEMIO_0 0

View File

@ -555,6 +555,7 @@ struct gcr3_tbl_info {
};
struct amd_io_pgtable {
seqcount_t seqcount; /* Protects root/mode update */
struct io_pgtable pgtbl;
int mode;
u64 *root;

View File

@ -1455,12 +1455,12 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu,
PCI_FUNC(e->devid));
devid = e->devid;
for (dev_i = devid_start; dev_i <= devid; ++dev_i) {
if (alias)
if (alias) {
for (dev_i = devid_start; dev_i <= devid; ++dev_i)
pci_seg->alias_table[dev_i] = devid_to;
set_dev_entry_from_acpi(iommu, devid_to, flags, ext_flags);
}
set_dev_entry_from_acpi_range(iommu, devid_start, devid, flags, ext_flags);
set_dev_entry_from_acpi(iommu, devid_to, flags, ext_flags);
break;
case IVHD_DEV_SPECIAL: {
u8 handle, type;
@ -3067,7 +3067,8 @@ static int __init early_amd_iommu_init(void)
if (!boot_cpu_has(X86_FEATURE_CX16)) {
pr_err("Failed to initialize. The CMPXCHG16B feature is required.\n");
return -EINVAL;
ret = -EINVAL;
goto out;
}
/*

View File

@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/dma-mapping.h>
#include <linux/seqlock.h>
#include <asm/barrier.h>
@ -130,8 +131,11 @@ static bool increase_address_space(struct amd_io_pgtable *pgtable,
*pte = PM_LEVEL_PDE(pgtable->mode, iommu_virt_to_phys(pgtable->root));
write_seqcount_begin(&pgtable->seqcount);
pgtable->root = pte;
pgtable->mode += 1;
write_seqcount_end(&pgtable->seqcount);
amd_iommu_update_and_flush_device_table(domain);
pte = NULL;
@ -153,6 +157,7 @@ static u64 *alloc_pte(struct amd_io_pgtable *pgtable,
{
unsigned long last_addr = address + (page_size - 1);
struct io_pgtable_cfg *cfg = &pgtable->pgtbl.cfg;
unsigned int seqcount;
int level, end_lvl;
u64 *pte, *page;
@ -170,8 +175,14 @@ static u64 *alloc_pte(struct amd_io_pgtable *pgtable,
}
level = pgtable->mode - 1;
pte = &pgtable->root[PM_LEVEL_INDEX(level, address)];
do {
seqcount = read_seqcount_begin(&pgtable->seqcount);
level = pgtable->mode - 1;
pte = &pgtable->root[PM_LEVEL_INDEX(level, address)];
} while (read_seqcount_retry(&pgtable->seqcount, seqcount));
address = PAGE_SIZE_ALIGN(address, page_size);
end_lvl = PAGE_SIZE_LEVEL(page_size);
@ -249,6 +260,7 @@ static u64 *fetch_pte(struct amd_io_pgtable *pgtable,
unsigned long *page_size)
{
int level;
unsigned int seqcount;
u64 *pte;
*page_size = 0;
@ -256,8 +268,12 @@ static u64 *fetch_pte(struct amd_io_pgtable *pgtable,
if (address > PM_LEVEL_SIZE(pgtable->mode))
return NULL;
level = pgtable->mode - 1;
pte = &pgtable->root[PM_LEVEL_INDEX(level, address)];
do {
seqcount = read_seqcount_begin(&pgtable->seqcount);
level = pgtable->mode - 1;
pte = &pgtable->root[PM_LEVEL_INDEX(level, address)];
} while (read_seqcount_retry(&pgtable->seqcount, seqcount));
*page_size = PTE_LEVEL_PAGE_SIZE(level);
while (level > 0) {
@ -541,6 +557,7 @@ static struct io_pgtable *v1_alloc_pgtable(struct io_pgtable_cfg *cfg, void *coo
if (!pgtable->root)
return NULL;
pgtable->mode = PAGE_MODE_3_LEVEL;
seqcount_init(&pgtable->seqcount);
cfg->pgsize_bitmap = amd_iommu_pgsize_bitmap;
cfg->ias = IOMMU_IN_ADDR_BIT_SIZE;

View File

@ -1575,6 +1575,10 @@ static void switch_to_super_page(struct dmar_domain *domain,
unsigned long lvl_pages = lvl_to_nr_pages(level);
struct dma_pte *pte = NULL;
if (WARN_ON(!IS_ALIGNED(start_pfn, lvl_pages) ||
!IS_ALIGNED(end_pfn + 1, lvl_pages)))
return;
while (start_pfn <= end_pfn) {
if (!pte)
pte = pfn_to_dma_pte(domain, start_pfn, &level,
@ -1650,7 +1654,8 @@ __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
unsigned long pages_to_remove;
pteval |= DMA_PTE_LARGE_PAGE;
pages_to_remove = min_t(unsigned long, nr_pages,
pages_to_remove = min_t(unsigned long,
round_down(nr_pages, lvl_pages),
nr_pte_to_next_page(pte) * lvl_pages);
end_pfn = iov_pfn + pages_to_remove - 1;
switch_to_super_page(domain, iov_pfn, end_pfn, largepage_lvl);

View File

@ -612,6 +612,23 @@ static u64 get_iota_region_flag(struct s390_domain *domain)
}
}
static bool reg_ioat_propagate_error(int cc, u8 status)
{
/*
* If the device is in the error state the reset routine
* will register the IOAT of the newly set domain on re-enable
*/
if (cc == ZPCI_CC_ERR && status == ZPCI_PCI_ST_FUNC_NOT_AVAIL)
return false;
/*
* If the device was removed treat registration as success
* and let the subsequent error event trigger tear down.
*/
if (cc == ZPCI_CC_INVAL_HANDLE)
return false;
return cc != ZPCI_CC_OK;
}
static int s390_iommu_domain_reg_ioat(struct zpci_dev *zdev,
struct iommu_domain *domain, u8 *status)
{
@ -696,7 +713,7 @@ static int s390_iommu_attach_device(struct iommu_domain *domain,
/* If we fail now DMA remains blocked via blocking domain */
cc = s390_iommu_domain_reg_ioat(zdev, domain, &status);
if (cc && status != ZPCI_PCI_ST_FUNC_NOT_AVAIL)
if (reg_ioat_propagate_error(cc, status))
return -EIO;
zdev->dma_table = s390_domain->dma_table;
zdev_s390_domain_update(zdev, domain);
@ -1032,7 +1049,8 @@ struct zpci_iommu_ctrs *zpci_get_iommu_ctrs(struct zpci_dev *zdev)
lockdep_assert_held(&zdev->dom_lock);
if (zdev->s390_domain->type == IOMMU_DOMAIN_BLOCKED)
if (zdev->s390_domain->type == IOMMU_DOMAIN_BLOCKED ||
zdev->s390_domain->type == IOMMU_DOMAIN_IDENTITY)
return NULL;
s390_domain = to_s390_domain(zdev->s390_domain);
@ -1123,12 +1141,7 @@ static int s390_attach_dev_identity(struct iommu_domain *domain,
/* If we fail now DMA remains blocked via blocking domain */
cc = s390_iommu_domain_reg_ioat(zdev, domain, &status);
/*
* If the device is undergoing error recovery the reset code
* will re-establish the new domain.
*/
if (cc && status != ZPCI_PCI_ST_FUNC_NOT_AVAIL)
if (reg_ioat_propagate_error(cc, status))
return -EIO;
zdev_s390_domain_update(zdev, domain);