IOMMU Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH 0/4] iommu/amd: re-mapping fixes
@ 2019-09-13 14:42 Andrei Dulea via iommu
  2019-09-13 14:42 ` [PATCH 1/4] iommu/amd: Fix pages leak in free_pagetable() Andrei Dulea via iommu
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Andrei Dulea via iommu @ 2019-09-13 14:42 UTC (permalink / raw)
  To: Joerg Roedel; +Cc: iommu, Jan H. Schönherr, Andrei Dulea

This patch series tries to address a few issues encountered when
replacing existing mappings:
.> pages leak in free_pagetable()
.> allow downgrading default page-sizes in alloc_pte()
.> tear-down all the replicated PTEs of a large mapping when downgrading
to smaller mappings

Andrei Dulea (4):
  iommu/amd: Fix pages leak in free_pagetable()
  iommu/amd: Fix downgrading default page-sizes in alloc_pte()
  iommu/amd: Introduce first_pte_l7() helper
  iommu/amd: Unmap all L7 PTEs when downgrading page-sizes

 drivers/iommu/amd_iommu.c | 73 +++++++++++++++++++++++++++++++--------
 1 file changed, 58 insertions(+), 15 deletions(-)

-- 
2.17.1

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/4] iommu/amd: Fix pages leak in free_pagetable()
  2019-09-13 14:42 [PATCH 0/4] iommu/amd: re-mapping fixes Andrei Dulea via iommu
@ 2019-09-13 14:42 ` Andrei Dulea via iommu
  2019-09-24  9:21   ` Joerg Roedel
  2019-09-13 14:42 ` [PATCH 2/4] iommu/amd: Fix downgrading default page-sizes in alloc_pte() Andrei Dulea via iommu
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 6+ messages in thread
From: Andrei Dulea via iommu @ 2019-09-13 14:42 UTC (permalink / raw)
  To: Joerg Roedel; +Cc: iommu, Jan H. Schönherr, Andrei Dulea

Take into account the gathered freelist in free_sub_pt(), otherwise we
end up leaking all that pages.

Fixes: 409afa44f9ba ("iommu/amd: Introduce free_sub_pt() function")
Signed-off-by: Andrei Dulea <adulea@amazon.de>
---
 drivers/iommu/amd_iommu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 1ed3b98324ba..138547446345 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -1425,7 +1425,7 @@ static void free_pagetable(struct protection_domain *domain)
 	BUG_ON(domain->mode < PAGE_MODE_NONE ||
 	       domain->mode > PAGE_MODE_6_LEVEL);
 
-	free_sub_pt(root, domain->mode, freelist);
+	freelist = free_sub_pt(root, domain->mode, freelist);
 
 	free_page_list(freelist);
 }
-- 
2.17.1

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 2/4] iommu/amd: Fix downgrading default page-sizes in alloc_pte()
  2019-09-13 14:42 [PATCH 0/4] iommu/amd: re-mapping fixes Andrei Dulea via iommu
  2019-09-13 14:42 ` [PATCH 1/4] iommu/amd: Fix pages leak in free_pagetable() Andrei Dulea via iommu
@ 2019-09-13 14:42 ` Andrei Dulea via iommu
  2019-09-13 14:42 ` [PATCH 3/4] iommu/amd: Introduce first_pte_l7() helper Andrei Dulea via iommu
  2019-09-13 14:42 ` [PATCH 4/4] iommu/amd: Unmap all L7 PTEs when downgrading page-sizes Andrei Dulea via iommu
  3 siblings, 0 replies; 6+ messages in thread
From: Andrei Dulea via iommu @ 2019-09-13 14:42 UTC (permalink / raw)
  To: Joerg Roedel; +Cc: iommu, Jan H. Schönherr, Andrei Dulea

Downgrading an existing large mapping to a mapping using smaller
page-sizes works only for the mappings created with page-mode 7 (i.e.
non-default page size).

Treat large mappings created with page-mode 0 (i.e. default page size)
like a non-present mapping and allow to overwrite it in alloc_pte().

While around, make sure that we flush the TLB only if we change an
existing mapping, otherwise we might end up acting on garbage PTEs.

Signed-off-by: Andrei Dulea <adulea@amazon.de>
---
 drivers/iommu/amd_iommu.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 138547446345..c7e28a8d25d1 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -1490,6 +1490,7 @@ static u64 *alloc_pte(struct protection_domain *domain,
 		pte_level = PM_PTE_LEVEL(__pte);
 
 		if (!IOMMU_PTE_PRESENT(__pte) ||
+		    pte_level == PAGE_MODE_NONE ||
 		    pte_level == PAGE_MODE_7_LEVEL) {
 			page = (u64 *)get_zeroed_page(gfp);
 			if (!page)
@@ -1500,7 +1501,7 @@ static u64 *alloc_pte(struct protection_domain *domain,
 			/* pte could have been changed somewhere. */
 			if (cmpxchg64(pte, __pte, __npte) != __pte)
 				free_page((unsigned long)page);
-			else if (pte_level == PAGE_MODE_7_LEVEL)
+			else if (IOMMU_PTE_PRESENT(__pte))
 				domain->updated = true;
 
 			continue;
-- 
2.17.1

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 3/4] iommu/amd: Introduce first_pte_l7() helper
  2019-09-13 14:42 [PATCH 0/4] iommu/amd: re-mapping fixes Andrei Dulea via iommu
  2019-09-13 14:42 ` [PATCH 1/4] iommu/amd: Fix pages leak in free_pagetable() Andrei Dulea via iommu
  2019-09-13 14:42 ` [PATCH 2/4] iommu/amd: Fix downgrading default page-sizes in alloc_pte() Andrei Dulea via iommu
@ 2019-09-13 14:42 ` Andrei Dulea via iommu
  2019-09-13 14:42 ` [PATCH 4/4] iommu/amd: Unmap all L7 PTEs when downgrading page-sizes Andrei Dulea via iommu
  3 siblings, 0 replies; 6+ messages in thread
From: Andrei Dulea via iommu @ 2019-09-13 14:42 UTC (permalink / raw)
  To: Joerg Roedel; +Cc: iommu, Jan H. Schönherr, Andrei Dulea

Given an arbitrary pte that is part of a large mapping, this function
returns the first pte of the series (and optionally the mapped size and
number of PTEs)
It will be re-used in a subsequent patch to replace an existing L7
mapping.

Signed-off-by: Andrei Dulea <adulea@amazon.de>
---
 drivers/iommu/amd_iommu.c | 40 ++++++++++++++++++++++++++++-----------
 1 file changed, 29 insertions(+), 11 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index c7e28a8d25d1..a227e7a9b8b7 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -501,6 +501,29 @@ static void iommu_uninit_device(struct device *dev)
 	 */
 }
 
+/*
+ * Helper function to get the first pte of a large mapping
+ */
+static u64 *first_pte_l7(u64 *pte, unsigned long *page_size,
+			 unsigned long *count)
+{
+	unsigned long pte_mask, pg_size, cnt;
+	u64 *fpte;
+
+	pg_size  = PTE_PAGE_SIZE(*pte);
+	cnt      = PAGE_SIZE_PTE_COUNT(pg_size);
+	pte_mask = ~((cnt << 3) - 1);
+	fpte     = (u64 *)(((unsigned long)pte) & pte_mask);
+
+	if (page_size)
+		*page_size = pg_size;
+
+	if (count)
+		*count = cnt;
+
+	return fpte;
+}
+
 /****************************************************************************
  *
  * Interrupt handling functions
@@ -1567,17 +1590,12 @@ static u64 *fetch_pte(struct protection_domain *domain,
 		*page_size = PTE_LEVEL_PAGE_SIZE(level);
 	}
 
-	if (PM_PTE_LEVEL(*pte) == 0x07) {
-		unsigned long pte_mask;
-
-		/*
-		 * If we have a series of large PTEs, make
-		 * sure to return a pointer to the first one.
-		 */
-		*page_size = pte_mask = PTE_PAGE_SIZE(*pte);
-		pte_mask   = ~((PAGE_SIZE_PTE_COUNT(pte_mask) << 3) - 1);
-		pte        = (u64 *)(((unsigned long)pte) & pte_mask);
-	}
+	/*
+	 * If we have a series of large PTEs, make
+	 * sure to return a pointer to the first one.
+	 */
+	if (PM_PTE_LEVEL(*pte) == PAGE_MODE_7_LEVEL)
+		pte = first_pte_l7(pte, page_size, NULL);
 
 	return pte;
 }
-- 
2.17.1

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 4/4] iommu/amd: Unmap all L7 PTEs when downgrading page-sizes
  2019-09-13 14:42 [PATCH 0/4] iommu/amd: re-mapping fixes Andrei Dulea via iommu
                   ` (2 preceding siblings ...)
  2019-09-13 14:42 ` [PATCH 3/4] iommu/amd: Introduce first_pte_l7() helper Andrei Dulea via iommu
@ 2019-09-13 14:42 ` Andrei Dulea via iommu
  3 siblings, 0 replies; 6+ messages in thread
From: Andrei Dulea via iommu @ 2019-09-13 14:42 UTC (permalink / raw)
  To: Joerg Roedel; +Cc: iommu, Jan H. Schönherr, Andrei Dulea

When replacing a large mapping created with page-mode 7 (i.e.
non-default page size), tear down the entire series of replicated PTEs.
Besides providing access to the old mapping, another thing that might go
wrong with this issue is on the fetch_pte() code path that can return a
PDE entry of the newly re-mapped range.

While at it, make sure that we flush the TLB in case alloc_pte() fails
and returns NULL at a lower level.

Fixes: 6d568ef9a622 ("iommu/amd: Allow downgrading page-sizes in alloc_pte()")
Signed-off-by: Andrei Dulea <adulea@amazon.de>
---
 drivers/iommu/amd_iommu.c | 30 +++++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index a227e7a9b8b7..fda9923542c9 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -1512,10 +1512,32 @@ static u64 *alloc_pte(struct protection_domain *domain,
 		__pte     = *pte;
 		pte_level = PM_PTE_LEVEL(__pte);
 
-		if (!IOMMU_PTE_PRESENT(__pte) ||
-		    pte_level == PAGE_MODE_NONE ||
+		/*
+		 * If we replace a series of large PTEs, we need
+		 * to tear down all of them.
+		 */
+		if (IOMMU_PTE_PRESENT(__pte) &&
 		    pte_level == PAGE_MODE_7_LEVEL) {
+			unsigned long count, i;
+			u64 *lpte;
+
+			lpte = first_pte_l7(pte, NULL, &count);
+
+			/*
+			 * Unmap the replicated PTEs that still match the
+			 * original large mapping
+			 */
+			for (i = 0; i < count; ++i)
+				cmpxchg64(&lpte[i], __pte, 0ULL);
+
+			domain->updated = true;
+			continue;
+		}
+
+		if (!IOMMU_PTE_PRESENT(__pte) ||
+		    pte_level == PAGE_MODE_NONE) {
 			page = (u64 *)get_zeroed_page(gfp);
+
 			if (!page)
 				return NULL;
 
@@ -1646,8 +1668,10 @@ static int iommu_map_page(struct protection_domain *dom,
 	count = PAGE_SIZE_PTE_COUNT(page_size);
 	pte   = alloc_pte(dom, bus_addr, page_size, NULL, gfp);
 
-	if (!pte)
+	if (!pte) {
+		update_domain(dom);
 		return -ENOMEM;
+	}
 
 	for (i = 0; i < count; ++i)
 		freelist = free_clear_pte(&pte[i], pte[i], freelist);
-- 
2.17.1

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/4] iommu/amd: Fix pages leak in free_pagetable()
  2019-09-13 14:42 ` [PATCH 1/4] iommu/amd: Fix pages leak in free_pagetable() Andrei Dulea via iommu
@ 2019-09-24  9:21   ` Joerg Roedel
  0 siblings, 0 replies; 6+ messages in thread
From: Joerg Roedel @ 2019-09-24  9:21 UTC (permalink / raw)
  To: Andrei Dulea; +Cc: iommu, Jan H. Schönherr

Hi Andrei,

On Fri, Sep 13, 2019 at 04:42:28PM +0200, Andrei Dulea wrote:
> Take into account the gathered freelist in free_sub_pt(), otherwise we
> end up leaking all that pages.
> 
> Fixes: 409afa44f9ba ("iommu/amd: Introduce free_sub_pt() function")
> Signed-off-by: Andrei Dulea <adulea@amazon.de>
> ---
>  drivers/iommu/amd_iommu.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
> index 1ed3b98324ba..138547446345 100644
> --- a/drivers/iommu/amd_iommu.c
> +++ b/drivers/iommu/amd_iommu.c
> @@ -1425,7 +1425,7 @@ static void free_pagetable(struct protection_domain *domain)
>  	BUG_ON(domain->mode < PAGE_MODE_NONE ||
>  	       domain->mode > PAGE_MODE_6_LEVEL);
>  
> -	free_sub_pt(root, domain->mode, freelist);
> +	freelist = free_sub_pt(root, domain->mode, freelist);

What a stupid mistake, thanks for fixing this. That is a clear example
which shows that we need more reviewers. Applied the whole series.


Thanks again,

       Joerg

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, back to index

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-13 14:42 [PATCH 0/4] iommu/amd: re-mapping fixes Andrei Dulea via iommu
2019-09-13 14:42 ` [PATCH 1/4] iommu/amd: Fix pages leak in free_pagetable() Andrei Dulea via iommu
2019-09-24  9:21   ` Joerg Roedel
2019-09-13 14:42 ` [PATCH 2/4] iommu/amd: Fix downgrading default page-sizes in alloc_pte() Andrei Dulea via iommu
2019-09-13 14:42 ` [PATCH 3/4] iommu/amd: Introduce first_pte_l7() helper Andrei Dulea via iommu
2019-09-13 14:42 ` [PATCH 4/4] iommu/amd: Unmap all L7 PTEs when downgrading page-sizes Andrei Dulea via iommu

IOMMU Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-iommu/0 linux-iommu/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-iommu linux-iommu/ https://lore.kernel.org/linux-iommu \
		iommu@lists.linux-foundation.org
	public-inbox-index linux-iommu

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.linux-foundation.lists.iommu


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git