[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAEHaoC0Au_0yLRKSnDg=_qjnmDMmbJ=Nff+W1feEBTib0Jb_gA@mail.gmail.com>
Date: Sun, 3 Aug 2025 14:09:38 +0300
From: Constantine Gavrilov <cgavrilov@...inidat.com>
To: linux-kernel@...r.kernel.org, Marek Szyprowski <m.szyprowski@...sung.com>,
Robin Murphy <robin.murphy@....com>, iommu@...ts.linux.dev
Subject: [PATCH 3/8] Large DMA alloc/add busy_regions sysfs attribute
This is the fourth patch from the set of patches that enable large IOMMU
DMA registrations. Entire work is available at the master branch of the
master branch of git@...hub.com:cgavrilov/linux.git repo.
Add busy_regions SYSFS attribute to IOMMU group. This allows to see used
addresses and debug failed allocations.
commit b01feb650dc080f268adb5ff26bda1b9bf2193a1
Author: Constantine Gavrilov <cgavrilov@...inidat.com>
Date: Wed Jun 25 19:49:16 2025 +0300
Add busy_regions sysfs attribute to IOMMU group.
This attribute shows allocated DMA regions for the group.
Add exported function iovad_show_busy_regions() to allow other
implementations.
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 0b7537e9812f..6ba9be4fb64d 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -761,6 +761,14 @@ static iova_align_t
dma_info_to_alignment(unsigned long attrs)
return align;
}
+ssize_t iommu_domain_show_busy_regions(struct iommu_domain *domain, char *buf)
+{
+ struct iommu_dma_cookie *cookie = domain->iova_cookie;
+ struct iova_domain *iovad = &cookie->iovad;
+
+ return iovad_show_busy_regions(iovad, buf);
+}
+
static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain *domain,
size_t size, u64 dma_limit, struct device *dev, iova_align_t align)
{
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index a4b606c591da..5daeb86a4aef 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -34,7 +34,9 @@
#include <linux/sched/mm.h>
#include <linux/msi.h>
#include <uapi/linux/iommufd.h>
-
+#ifdef CONFIG_IOMMU_DMA
+#include <linux/iommu-dma.h>
+#endif
#include "dma-iommu.h"
#include "iommu-priv.h"
@@ -927,6 +929,19 @@ static ssize_t
iommu_group_show_resv_regions(struct iommu_group *group,
return offset;
}
+#ifdef CONFIG_IOMMU_DMA
+static ssize_t iommu_group_show_busy_regions(struct iommu_group *group,
+ char *buf)
+{
+ if (!group->domain)
+ return 0;
+
+ return iommu_domain_show_busy_regions(group->domain, buf);
+} int off = 0;
+
+
+#endif
+
static ssize_t iommu_group_show_type(struct iommu_group *group,
char *buf)
{
@@ -962,6 +977,11 @@ static IOMMU_GROUP_ATTR(name, S_IRUGO,
iommu_group_show_name, NULL);
static IOMMU_GROUP_ATTR(reserved_regions, 0444,
iommu_group_show_resv_regions, NULL);
+#ifdef CONFIG_IOMMU_DMA
+static IOMMU_GROUP_ATTR(busy_regions, 0444,
+ iommu_group_show_busy_regions, NULL);
+#endif
+
static IOMMU_GROUP_ATTR(type, 0644, iommu_group_show_type,
iommu_group_store_type);
@@ -1049,6 +1069,15 @@ struct iommu_group *iommu_group_alloc(void)
return ERR_PTR(ret);
}
+#ifdef CONFIG_IOMMU_DMA
+ ret = iommu_group_create_file(group,
+ &iommu_group_attr_busy_regions);
+ if (ret) {
+ kobject_put(group->devices_kobj);
+ return ERR_PTR(ret);
+ }
+#endif
+
ret = iommu_group_create_file(group, &iommu_group_attr_type);
if (ret) {
kobject_put(group->devices_kobj);
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
index 41d5d34fcc33..96144c58b386 100644
--- a/drivers/iommu/iova.c
+++ b/drivers/iommu/iova.c
@@ -280,6 +280,37 @@ alloc_iova(struct iova_domain *iovad, unsigned long size,
}
EXPORT_SYMBOL_GPL(alloc_iova);
+/*
+ * Helper function to output allocated regions to a buffer.
+ * Can be used as a show function for a sysfs attribute.
+ * buf is page aigned buffer of PAGE_SIZE.
+*/
+ssize_t iovad_show_busy_regions(struct iova_domain *iovad, char *buf)
+{
+ int off = 0;
+ struct rb_node *curr;
+ struct iova *curr_iova;
+ unsigned long flags;
+ unsigned long shift = iova_shift(iovad);
+
+ spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
+ curr = &iovad->anchor.node;
+ /* skip ancor node, it has pfn_hi = pfn_lo = IOVA_ANCHOR = -1LU */
+ curr = rb_prev(curr);
+ while(curr) {
+ curr_iova = rb_entry(curr, struct iova, node);
+ off += sysfs_emit_at(buf, off, "0x%016lx-0x%016lx\n",
curr_iova->pfn_lo << shift,
+ ((curr_iova->pfn_hi + 1) << shift) - 1);
+ curr = rb_prev(curr);
+ /* do not iterate further if the page is full */
+ if (off >= (PAGE_SIZE - 38))
+ break;
+ }
+ spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
+ return off;
+}
+EXPORT_SYMBOL_GPL(iovad_show_busy_regions);
+
static struct iova *
private_find_iova(struct iova_domain *iovad, unsigned long pfn)
{
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 156732807994..5fe92c00221d 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -1511,6 +1511,7 @@ static inline void iommu_debugfs_setup(void) {}
#ifdef CONFIG_IOMMU_DMA
int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base);
+ssize_t iommu_domain_show_busy_regions(struct iommu_domain *domain, char *buf);
#else /* CONFIG_IOMMU_DMA */
static inline int iommu_get_msi_cookie(struct iommu_domain *domain,
dma_addr_t base)
{
diff --git a/include/linux/iova.h b/include/linux/iova.h
index e35762c0acdb..c09d224cce2b 100644
--- a/include/linux/iova.h
+++ b/include/linux/iova.h
@@ -91,8 +91,12 @@ void __free_iova(struct iova_domain *iovad, struct
iova *iova);
struct iova *alloc_iova(struct iova_domain *iovad, unsigned long size,
unsigned long limit_pfn,
iova_align_t align);
+
void free_iova_fast(struct iova_domain *iovad, unsigned long pfn,
unsigned long size);
+
+ssize_t iovad_show_busy_regions(struct iova_domain *iovad, char *buf);
+
unsigned long alloc_iova_fast(struct iova_domain *iovad, unsigned long size,
unsigned long limit_pfn, bool flush_rcache,
iova_align_t align);
struct iova *reserve_iova(struct iova_domain *iovad, unsigned long pfn_lo,
@@ -120,6 +124,11 @@ static inline void __free_iova(struct iova_domain
*iovad, struct iova *iova)
{
}
+ssize_t iovad_show_busy_regions(struct iova_domain *iovad, char *buf)
+{
+ return -ENOTSUPP;
+}
+
static inline struct iova *alloc_iova(struct iova_domain *iovad,
unsigned long size,
unsigned long limit_pfn,
--
----------------------------------------
Constantine Gavrilov
System Architect and Platform Engineer
Infinidat
----------------------------------------
Powered by blists - more mailing lists