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:
commit
497b9a7b8d
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue