Skip to content

Commit 7e84593

Browse files
misalehjoergroedel
authored andcommitted
iommu: debug-pagealloc: Track IOMMU pages
Using the new calls, use an atomic refcount to track how many times a page is mapped in any of the IOMMUs. For unmap we need to use iova_to_phys() to get the physical address of the pages. We use the smallest supported page size as the granularity of tracking per domain. This is important as it is possible to map pages and unmap them with larger sizes (as in map_sg()) cases. Reviewed-by: Samiullah Khawaja <skhawaja@google.com> Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com> Signed-off-by: Mostafa Saleh <smostafa@google.com> Reviewed-by: Pranjal Shrivastava <praan@google.com> Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
1 parent ccc2121 commit 7e84593

1 file changed

Lines changed: 84 additions & 0 deletions

File tree

drivers/iommu/iommu-debug-pagealloc.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,103 @@ struct page_ext_operations page_iommu_debug_ops = {
2929
.need = need_iommu_debug,
3030
};
3131

32+
static struct page_ext *get_iommu_page_ext(phys_addr_t phys)
33+
{
34+
struct page *page = phys_to_page(phys);
35+
struct page_ext *page_ext = page_ext_get(page);
36+
37+
return page_ext;
38+
}
39+
40+
static struct iommu_debug_metadata *get_iommu_data(struct page_ext *page_ext)
41+
{
42+
return page_ext_data(page_ext, &page_iommu_debug_ops);
43+
}
44+
45+
static void iommu_debug_inc_page(phys_addr_t phys)
46+
{
47+
struct page_ext *page_ext = get_iommu_page_ext(phys);
48+
struct iommu_debug_metadata *d = get_iommu_data(page_ext);
49+
50+
WARN_ON(atomic_inc_return_relaxed(&d->ref) <= 0);
51+
page_ext_put(page_ext);
52+
}
53+
54+
static void iommu_debug_dec_page(phys_addr_t phys)
55+
{
56+
struct page_ext *page_ext = get_iommu_page_ext(phys);
57+
struct iommu_debug_metadata *d = get_iommu_data(page_ext);
58+
59+
WARN_ON(atomic_dec_return_relaxed(&d->ref) < 0);
60+
page_ext_put(page_ext);
61+
}
62+
63+
/*
64+
* IOMMU page size doesn't have to match the CPU page size. So, we use
65+
* the smallest IOMMU page size to refcount the pages in the vmemmap.
66+
* That is important as both map and unmap has to use the same page size
67+
* to update the refcount to avoid double counting the same page.
68+
* And as we can't know from iommu_unmap() what was the original page size
69+
* used for map, we just use the minimum supported one for both.
70+
*/
71+
static size_t iommu_debug_page_size(struct iommu_domain *domain)
72+
{
73+
return 1UL << __ffs(domain->pgsize_bitmap);
74+
}
75+
3276
void __iommu_debug_map(struct iommu_domain *domain, phys_addr_t phys, size_t size)
3377
{
78+
size_t off, end;
79+
size_t page_size = iommu_debug_page_size(domain);
80+
81+
if (WARN_ON(!phys || check_add_overflow(phys, size, &end)))
82+
return;
83+
84+
for (off = 0 ; off < size ; off += page_size) {
85+
if (!pfn_valid(__phys_to_pfn(phys + off)))
86+
continue;
87+
iommu_debug_inc_page(phys + off);
88+
}
89+
}
90+
91+
static void __iommu_debug_update_iova(struct iommu_domain *domain,
92+
unsigned long iova, size_t size, bool inc)
93+
{
94+
size_t off, end;
95+
size_t page_size = iommu_debug_page_size(domain);
96+
97+
if (WARN_ON(check_add_overflow(iova, size, &end)))
98+
return;
99+
100+
for (off = 0 ; off < size ; off += page_size) {
101+
phys_addr_t phys = iommu_iova_to_phys(domain, iova + off);
102+
103+
if (!phys || !pfn_valid(__phys_to_pfn(phys)))
104+
continue;
105+
106+
if (inc)
107+
iommu_debug_inc_page(phys);
108+
else
109+
iommu_debug_dec_page(phys);
110+
}
34111
}
35112

36113
void __iommu_debug_unmap_begin(struct iommu_domain *domain,
37114
unsigned long iova, size_t size)
38115
{
116+
__iommu_debug_update_iova(domain, iova, size, false);
39117
}
40118

41119
void __iommu_debug_unmap_end(struct iommu_domain *domain,
42120
unsigned long iova, size_t size,
43121
size_t unmapped)
44122
{
123+
if ((unmapped == size) || WARN_ON_ONCE(unmapped > size))
124+
return;
125+
126+
/* If unmap failed, re-increment the refcount. */
127+
__iommu_debug_update_iova(domain, iova + unmapped,
128+
size - unmapped, true);
45129
}
46130

47131
void iommu_debug_init(void)

0 commit comments

Comments
 (0)