Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6f820bb9 authored by Joerg Roedel's avatar Joerg Roedel
Browse files

iommu/amd: Allow to upgrade page-size



Before this patch the iommu_map_page() function failed when
it tried to map a huge-page where smaller mappings existed
before.

With this change the page-table pages of the old mappings
are teared down, so that the huge-page can be mapped.

Signed-off-by: default avatarJoerg Roedel <jroedel@suse.de>
parent 9db034d5
Loading
Loading
Loading
Loading
+27 −2
Original line number Diff line number Diff line
@@ -1557,6 +1557,25 @@ static u64 *fetch_pte(struct protection_domain *domain,
	return pte;
}

static struct page *free_clear_pte(u64 *pte, u64 pteval, struct page *freelist)
{
	unsigned long pt;
	int mode;

	while (cmpxchg64(pte, pteval, 0) != pteval) {
		pr_warn("AMD-Vi: IOMMU pte changed since we read it\n");
		pteval = *pte;
	}

	if (!IOMMU_PTE_PRESENT(pteval))
		return freelist;

	pt   = (unsigned long)IOMMU_PTE_PAGE(pteval);
	mode = IOMMU_PTE_MODE(pteval);

	return free_sub_pt(pt, mode, freelist);
}

/*
 * Generic mapping functions. It maps a physical address into a DMA
 * address space. It allocates the page table pages if necessary.
@@ -1571,6 +1590,7 @@ static int iommu_map_page(struct protection_domain *dom,
			  int prot,
			  gfp_t gfp)
{
	struct page *freelist = NULL;
	u64 __pte, *pte;
	int i, count;

@@ -1587,8 +1607,10 @@ static int iommu_map_page(struct protection_domain *dom,
		return -ENOMEM;

	for (i = 0; i < count; ++i)
		if (IOMMU_PTE_PRESENT(pte[i]))
			return -EBUSY;
		freelist = free_clear_pte(&pte[i], pte[i], freelist);

	if (freelist != NULL)
		dom->updated = true;

	if (count > 1) {
		__pte = PAGE_SIZE_PTE(__sme_set(phys_addr), page_size);
@@ -1606,6 +1628,9 @@ static int iommu_map_page(struct protection_domain *dom,

	update_domain(dom);

	/* Everything flushed out, free pages now */
	free_page_list(freelist);

	return 0;
}