FAILED: patch "[PATCH] iommu/amd/pgtbl: Fix possible race while increase page table" failed to apply to 5.15-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x 1e56310b40fd2e7e0b9493da9ff488af145bdd0c
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2025092103-skilled-expand-23a8@gregkh' --subject-prefix 'PATCH 5.15.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 1e56310b40fd2e7e0b9493da9ff488af145bdd0c Mon Sep 17 00:00:00 2001
From: Vasant Hegde <[email protected]>
Date: Sat, 13 Sep 2025 06:26:57 +0000
Subject: [PATCH] iommu/amd/pgtbl: Fix possible race while increase page table
level
The AMD IOMMU host page table implementation supports dynamic page table levels (up to 6 levels), starting with a 3-level configuration that expands based on IOVA address. The kernel maintains a root pointer and current page table level to enable proper page table walks in alloc_pte()/fetch_pte() operations.
The IOMMU IOVA allocator initially starts with 32-bit address and onces its exhuasted it switches to 64-bit address (max address is determined based on IOMMU and device DMA capability). To support larger IOVA, AMD IOMMU driver increases page table level.
But in unmap path (iommu_v1_unmap_pages()), fetch_pte() reads pgtable->[root/mode] without lock. So its possible that in exteme corner case, when increase_address_space() is updating pgtable->[root/mode], fetch_pte() reads wrong page table level (pgtable->mode). It does compare the value with level encoded in page table and returns NULL. This will result is iommu_unmap ops to fail and upper layer may retry/log WARN_ON.
CPU 0 CPU 1
------ ------ map pages unmap pages alloc_pte() -> increase_address_space() iommu_v1_unmap_pages() -> fetch_pte()
pgtable->root = pte (new root value)
READ pgtable->[mode/root]
Reads new root, old mode
Updates mode (pgtable->mode += 1)
Since Page table level updates are infrequent and already synchronized with a spinlock, implement seqcount to enable lock-free read operations on the read path.
drivers/iommu/amd/amd_iommu_types.h +1 -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;
drivers/iommu/amd/io_pgtable.c +21 -4
@@ -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 *cooif (!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 ;