* [RFC PATCH 0/6] iommufd: harden DMA-BUF PAL setup and release
@ 2026-10-20 6:37 Karl Mehltretter
2026-10-20 6:37 ` [RFC PATCH 1/6] iommufd: Preserve DMA-buf pin errors Karl Mehltretter
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-10-20 6:37 UTC (permalink / raw)
To: Jason Gunthorpe, Alistair Popple
Cc: Karl Mehltretter, Kevin Tian, Joerg Roedel, Will Deacon,
Robin Murphy, Shuah Khan, Christian König, Sumit Semwal,
Thomas Hellstrom, Vivek Kasireddy, dri-devel, iommu,
linux-kernel, linux-kselftest, linux-media, linaro-mm-sig
This is a narrow companion to Jason's unmerged DMA-BUF mapping-type RFC. It
fixes setup and release problems in the IOMMUFD PAL consumer and adds fault-
injection selftests. It is not standalone and requires this exact base:
c37cf2824740d ("cover-letter: Add DMA-buf mapping types and convert
vfio/iommufd to use them")
I used LLM assistance while inspecting, testing and drafting this series.
I also tested DMA-BUF paths on a Raspberry Pi 400 and Pi 500+, using their
stock Raspberry Pi kernels (6.18.39+rpt-rpi-v8 and 6.18.34+rpt-rpi-2712).
Both passed offscreen CPU/V3D byte checks. The Pi 400 also passed five raw-MJPG
payload-prefix comparisons through V3D; this was not direct or zero-copy camera
DMA. Neither board ran this RFC or IOMMUFD PAL. The Pi 500+ connector state and
mode remained unchanged; the tested buffers were not scanned out.
RFC:
https://lore.kernel.org/r/0-v1-b5cab63049c0+191af-dmabuf_map_type_jgg@nvidia.com/
Jason's branch:
https://github.com/jgunthorpe/linux/commits/dmabuf_map_type
This series:
https://github.com/kmehltretter82/linux/commits/dmabuf-pal-lifecycle-rfc-v1
Diederik's RK3588 testing produced a useful trace through
rockchip_gem_iommu_map() and iommu_map_sg():
https://lore.kernel.org/r/DLYLA3WQNN3X.3FB9MO8ZWBQ5@cknow-tech.com/
Jason noted that this showed the same private-IOMMU-domain mapping need as
IOMMUFD:
https://lore.kernel.org/r/20261009125338.GB13920@nvidia.com/
That trace is motivation only. This series neither revives my warn-only
mechanism nor converts Rockchip. Jason's feedback on that mechanism is here:
https://lore.kernel.org/r/20261009124259.GA13920@nvidia.com/
Compared with Jason's branch, this series:
- preserves dma_buf_pin() errors;
- publishes the attachment and successful PAL list together under resv;
- unmaps and clears the published PAL pointer under resv before unlocking;
- keeps callback state alive until dma_buf_detach() returns;
- calls dma_buf_unpin() while holding resv; and
- tests pin/map failures, cleanup counters and retry on the same DMA-BUF.
Patch 3 follows an earlier forced-window arm64 QEMU/KASAN double-free report:
https://lore.kernel.org/r/20261016194022.62065-1-kmehltretter@gmail.com/
The proposed release order is:
resv lock -> unmap and clear PAL -> unpin -> unlock -> detach -> put ->
destroy the pages mutex
Questions:
1. Is that publication and lifetime rule correct?
2. Is c37cf2824740d still the right base for this companion series? If not,
which tree should I use?
3. Can these lifecycle fixes stand independently of moving final mapping
selection closer to map time?
https://lore.kernel.org/r/20261016202653.97339-1-kmehltretter@gmail.com/
https://lore.kernel.org/r/20261019184123.GR13920@nvidia.com/
4. Is the per-buffer selftest control acceptable, or would deterministic
setup/release race hooks be more useful?
Testing:
- arm64 QEMU virt: two boots, 24 selected cases per boot, zero skips;
- x86-64 KVM q35: two 24-case candidate boots plus one focused four-case
pin-failure boot;
- a one-defect x86 control failed all four pin-failure variants on the
expected erroneous successful ioctl return;
- x86-64 GCC 13.3 built the exact head, headers and IOMMUFD selftests; this
build was not booted; and
- KASAN and lockdep initialized with no diagnostics in candidate runs.
The QEMU/KVM campaigns use mock IOMMU domains and the selftest exporter. They
do not cover a non-selftest PAL exporter, physical DMA/IOMMU, forced
invalidation races, or the complete IOMMUFD selftest suite. The x86 negative
control validates patch 1 only; the earlier patch-3 report is separate.
Testing with a non-selftest PAL exporter would be welcome, especially failure
followed by retry, invalidation during setup/release, repeated revoke, and
multi-range buffers. Please include the base, config, architecture,
exporter/device and full dmesg. A full IOMMUFD selftest run on another
configuration would also help.
Thanks,
Karl
Karl Mehltretter (6):
iommufd: Preserve DMA-buf pin errors
iommufd: Publish successful PAL mappings under resv
iommufd: Clear the PAL list before dropping resv
iommufd: Keep the pages mutex alive until DMA-buf detach
iommufd: Unpin the DMA-buf while holding resv
iommufd/selftest: Check DMA-buf pin and map failure cleanup
drivers/iommu/iommufd/iommufd_test.h | 24 ++++
drivers/iommu/iommufd/pages.c | 24 ++--
drivers/iommu/iommufd/selftest.c | 111 ++++++++++++++++
tools/testing/selftests/iommu/iommufd.c | 121 ++++++++++++++++++
tools/testing/selftests/iommu/iommufd_utils.h | 30 +++++
5 files changed, 298 insertions(+), 12 deletions(-)
base-commit: c37cf2824740d833b21eb816e21f94836100291f
--
2.39.5 (Apple Git-154)
^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC PATCH 1/6] iommufd: Preserve DMA-buf pin errors
2026-10-20 6:37 [RFC PATCH 0/6] iommufd: harden DMA-BUF PAL setup and release Karl Mehltretter
@ 2026-10-20 6:37 ` Karl Mehltretter
2026-10-20 6:37 ` [RFC PATCH 2/6] iommufd: Publish successful PAL mappings under resv Karl Mehltretter
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-10-20 6:37 UTC (permalink / raw)
To: Jason Gunthorpe, Alistair Popple
Cc: Karl Mehltretter, Kevin Tian, Joerg Roedel, Will Deacon,
Robin Murphy, Shuah Khan, Christian König, Sumit Semwal,
Thomas Hellstrom, Vivek Kasireddy, dri-devel, iommu,
linux-kernel, linux-kselftest, linux-media, linaro-mm-sig
If dma_buf_pin() fails, iopt_map_dmabuf() replaces its error with
PTR_ERR(pages->dmabuf.exp_phys). The newly allocated pages object still
has a NULL exp_phys, so the function detaches and returns zero. Its
caller then accepts a DMA-buf pages object with no attachment.
Keep the error returned by dma_buf_pin() when unwinding the attachment.
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
drivers/iommu/iommufd/pages.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c
index 98c2a8027..0b32c65af 100644
--- a/drivers/iommu/iommufd/pages.c
+++ b/drivers/iommu/iommufd/pages.c
@@ -1510,10 +1510,8 @@ static int iopt_map_dmabuf(struct iommufd_ctx *ictx, struct iopt_pages *pages,
}
rc = dma_buf_pin(attach);
- if (rc) {
- rc = PTR_ERR(pages->dmabuf.exp_phys);
+ if (rc)
goto err_detach;
- }
pages->dmabuf.exp_phys = dma_buf_pal_map_phys(attach);
if (IS_ERR(pages->dmabuf.exp_phys)) {
--
2.39.5 (Apple Git-154)
^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC PATCH 2/6] iommufd: Publish successful PAL mappings under resv
2026-10-20 6:37 [RFC PATCH 0/6] iommufd: harden DMA-BUF PAL setup and release Karl Mehltretter
2026-10-20 6:37 ` [RFC PATCH 1/6] iommufd: Preserve DMA-buf pin errors Karl Mehltretter
@ 2026-10-20 6:37 ` Karl Mehltretter
2026-10-20 6:37 ` [RFC PATCH 3/6] iommufd: Clear the PAL list before dropping resv Karl Mehltretter
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-10-20 6:37 UTC (permalink / raw)
To: Jason Gunthorpe, Alistair Popple
Cc: Karl Mehltretter, Kevin Tian, Joerg Roedel, Will Deacon,
Robin Murphy, Shuah Khan, Christian König, Sumit Semwal,
Thomas Hellstrom, Vivek Kasireddy, dri-devel, iommu,
linux-kernel, linux-kselftest, linux-media, linaro-mm-sig
iopt_map_dmabuf() stores the PAL map result in exp_phys before checking
for errors and publishes the attachment after dropping resv. The
attachment is already linked to the DMA-buf. An invalidation after the
unlock can therefore pass a NULL attachment to dma_buf_pal_unmap_phys().
On map failure, the non-NULL error pointer also looks like a live mapping
to iopt_dmabuf_revoked().
Keep the map result local until it succeeds. Publish the attachment and
physical list together before dropping resv, leaving exp_phys NULL on
failure.
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
drivers/iommu/iommufd/pages.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c
index 0b32c65af..9e9bc6a1c 100644
--- a/drivers/iommu/iommufd/pages.c
+++ b/drivers/iommu/iommufd/pages.c
@@ -1491,6 +1491,7 @@ static int iopt_map_dmabuf(struct iommufd_ctx *ictx, struct iopt_pages *pages,
{
struct dma_buf_mapping_match pal_match[] = { DMA_BUF_IMAPPING_PAL() };
struct dma_buf_attachment *attach;
+ struct dma_buf_phys_list *phys;
int rc;
attach = dma_buf_mapping_attach(dmabuf, pal_match,
@@ -1513,16 +1514,16 @@ static int iopt_map_dmabuf(struct iommufd_ctx *ictx, struct iopt_pages *pages,
if (rc)
goto err_detach;
- pages->dmabuf.exp_phys = dma_buf_pal_map_phys(attach);
- if (IS_ERR(pages->dmabuf.exp_phys)) {
- rc = PTR_ERR(pages->dmabuf.exp_phys);
+ phys = dma_buf_pal_map_phys(attach);
+ if (IS_ERR(phys)) {
+ rc = PTR_ERR(phys);
goto err_unpin;
}
- dma_resv_unlock(dmabuf->resv);
-
/* On success iopt_release_pages() will detach and put the dmabuf. */
pages->dmabuf.attach = attach;
+ pages->dmabuf.exp_phys = phys;
+ dma_resv_unlock(dmabuf->resv);
return 0;
err_unpin:
--
2.39.5 (Apple Git-154)
^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC PATCH 3/6] iommufd: Clear the PAL list before dropping resv
2026-10-20 6:37 [RFC PATCH 0/6] iommufd: harden DMA-BUF PAL setup and release Karl Mehltretter
2026-10-20 6:37 ` [RFC PATCH 1/6] iommufd: Preserve DMA-buf pin errors Karl Mehltretter
2026-10-20 6:37 ` [RFC PATCH 2/6] iommufd: Publish successful PAL mappings under resv Karl Mehltretter
@ 2026-10-20 6:37 ` Karl Mehltretter
2026-10-20 6:37 ` [RFC PATCH 4/6] iommufd: Keep the pages mutex alive until DMA-buf detach Karl Mehltretter
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-10-20 6:37 UTC (permalink / raw)
To: Jason Gunthorpe, Alistair Popple
Cc: Karl Mehltretter, Kevin Tian, Joerg Roedel, Will Deacon,
Robin Murphy, Shuah Khan, Christian König, Sumit Semwal,
Thomas Hellstrom, Vivek Kasireddy, dri-devel, iommu,
linux-kernel, linux-kselftest, linux-media, linaro-mm-sig
iopt_release_pages() frees the PAL physical list while holding resv but
leaves exp_phys pointing to it. Between the reservation unlock and
dma_buf_detach(), an invalidation can see that pointer and unmap the same
list again.
Clear exp_phys before dropping resv so a later invalidation sees that
there is no live PAL mapping.
Link: https://lore.kernel.org/r/20261016194022.62065-1-kmehltretter@gmail.com/
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
drivers/iommu/iommufd/pages.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c
index 9e9bc6a1c..76d8803d6 100644
--- a/drivers/iommu/iommufd/pages.c
+++ b/drivers/iommu/iommufd/pages.c
@@ -1674,9 +1674,11 @@ void iopt_release_pages(struct kref *kref)
struct dma_buf *dmabuf = pages->dmabuf.attach->dmabuf;
dma_resv_lock(dmabuf->resv, NULL);
- if (pages->dmabuf.exp_phys)
+ if (pages->dmabuf.exp_phys) {
dma_buf_pal_unmap_phys(pages->dmabuf.attach,
pages->dmabuf.exp_phys);
+ pages->dmabuf.exp_phys = NULL;
+ }
dma_resv_unlock(dmabuf->resv);
dma_buf_unpin(pages->dmabuf.attach);
--
2.39.5 (Apple Git-154)
^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC PATCH 4/6] iommufd: Keep the pages mutex alive until DMA-buf detach
2026-10-20 6:37 [RFC PATCH 0/6] iommufd: harden DMA-BUF PAL setup and release Karl Mehltretter
` (2 preceding siblings ...)
2026-10-20 6:37 ` [RFC PATCH 3/6] iommufd: Clear the PAL list before dropping resv Karl Mehltretter
@ 2026-10-20 6:37 ` Karl Mehltretter
2026-10-20 6:37 ` [RFC PATCH 5/6] iommufd: Unpin the DMA-buf while holding resv Karl Mehltretter
2026-10-20 6:37 ` [RFC PATCH 6/6] iommufd/selftest: Check DMA-buf pin and map failure cleanup Karl Mehltretter
5 siblings, 0 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-10-20 6:37 UTC (permalink / raw)
To: Jason Gunthorpe, Alistair Popple
Cc: Karl Mehltretter, Kevin Tian, Joerg Roedel, Will Deacon,
Robin Murphy, Shuah Khan, Christian König, Sumit Semwal,
Thomas Hellstrom, Vivek Kasireddy, dri-devel, iommu,
linux-kernel, linux-kselftest, linux-media, linaro-mm-sig
iopt_release_pages() destroys pages->mutex before removing the DMA-buf
attachment. The linked attachment still exposes pages through
importer_priv. An invalidation can enter iopt_revoke_notify() and lock
the destroyed mutex, including when exp_phys is already NULL.
Destroy pages->mutex after dma_buf_detach() has removed the attachment
and excluded further invalidation callbacks.
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
drivers/iommu/iommufd/pages.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c
index 76d8803d6..59ee68a1a 100644
--- a/drivers/iommu/iommufd/pages.c
+++ b/drivers/iommu/iommufd/pages.c
@@ -1667,7 +1667,6 @@ void iopt_release_pages(struct kref *kref)
WARN_ON(pages->npinned);
WARN_ON(!xa_empty(&pages->pinned_pfns));
mmdrop(pages->source_mm);
- mutex_destroy(&pages->mutex);
put_task_struct(pages->source_task);
free_uid(pages->source_user);
if (iopt_is_dmabuf(pages) && pages->dmabuf.attach) {
@@ -1689,6 +1688,7 @@ void iopt_release_pages(struct kref *kref)
} else if (pages->type == IOPT_ADDRESS_FILE) {
fput(pages->file);
}
+ mutex_destroy(&pages->mutex);
kfree(pages);
}
--
2.39.5 (Apple Git-154)
^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC PATCH 5/6] iommufd: Unpin the DMA-buf while holding resv
2026-10-20 6:37 [RFC PATCH 0/6] iommufd: harden DMA-BUF PAL setup and release Karl Mehltretter
` (3 preceding siblings ...)
2026-10-20 6:37 ` [RFC PATCH 4/6] iommufd: Keep the pages mutex alive until DMA-buf detach Karl Mehltretter
@ 2026-10-20 6:37 ` Karl Mehltretter
2026-10-20 6:37 ` [RFC PATCH 6/6] iommufd/selftest: Check DMA-buf pin and map failure cleanup Karl Mehltretter
5 siblings, 0 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-10-20 6:37 UTC (permalink / raw)
To: Jason Gunthorpe, Alistair Popple
Cc: Karl Mehltretter, Kevin Tian, Joerg Roedel, Will Deacon,
Robin Murphy, Shuah Khan, Christian König, Sumit Semwal,
Thomas Hellstrom, Vivek Kasireddy, dri-devel, iommu,
linux-kernel, linux-kselftest, linux-media, linaro-mm-sig
iopt_release_pages() calls dma_buf_unpin() after releasing the DMA-buf
reservation lock. The unpin interface requires the reservation to be
held while it invokes the exporter callback.
Move the unpin into the reservation critical section used to release
the PAL physical list.
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
drivers/iommu/iommufd/pages.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c
index 59ee68a1a..85004a936 100644
--- a/drivers/iommu/iommufd/pages.c
+++ b/drivers/iommu/iommufd/pages.c
@@ -1678,9 +1678,8 @@ void iopt_release_pages(struct kref *kref)
pages->dmabuf.exp_phys);
pages->dmabuf.exp_phys = NULL;
}
- dma_resv_unlock(dmabuf->resv);
-
dma_buf_unpin(pages->dmabuf.attach);
+ dma_resv_unlock(dmabuf->resv);
dma_buf_detach(dmabuf, pages->dmabuf.attach);
dma_buf_put(dmabuf);
--
2.39.5 (Apple Git-154)
^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC PATCH 6/6] iommufd/selftest: Check DMA-buf pin and map failure cleanup
2026-10-20 6:37 [RFC PATCH 0/6] iommufd: harden DMA-BUF PAL setup and release Karl Mehltretter
` (4 preceding siblings ...)
2026-10-20 6:37 ` [RFC PATCH 5/6] iommufd: Unpin the DMA-buf while holding resv Karl Mehltretter
@ 2026-10-20 6:37 ` Karl Mehltretter
5 siblings, 0 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-10-20 6:37 UTC (permalink / raw)
To: Jason Gunthorpe, Alistair Popple
Cc: Karl Mehltretter, Kevin Tian, Joerg Roedel, Will Deacon,
Robin Murphy, Shuah Khan, Christian König, Sumit Semwal,
Thomas Hellstrom, Vivek Kasireddy, dri-devel, iommu,
linux-kernel, linux-kselftest, linux-media, linaro-mm-sig
The PAL selftest exporter has no pin callback, so it cannot exercise the
importer's dma_buf_pin() error path. An error there must reach userspace
without mapping the buffer or unpinning an unsuccessful pin.
Add per-buffer error injection and reservation-protected lifecycle
counters. Check pin and map failure cleanup, then clear each fault and
retry the same buffer to verify balanced successful cleanup.
Also reject unknown control flags, nonzero reserved fields, invalid errno
values and configuration changes while an attachment exists.
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
drivers/iommu/iommufd/iommufd_test.h | 24 ++++
drivers/iommu/iommufd/selftest.c | 111 ++++++++++++++++
tools/testing/selftests/iommu/iommufd.c | 121 ++++++++++++++++++
tools/testing/selftests/iommu/iommufd_utils.h | 30 +++++
4 files changed, 286 insertions(+)
diff --git a/drivers/iommu/iommufd/iommufd_test.h b/drivers/iommu/iommufd/iommufd_test.h
index dae7d808b..d516a5aad 100644
--- a/drivers/iommu/iommufd/iommufd_test.h
+++ b/drivers/iommu/iommufd/iommufd_test.h
@@ -32,6 +32,7 @@ enum {
IOMMU_TEST_OP_DMABUF_GET,
IOMMU_TEST_OP_DMABUF_REVOKE,
IOMMU_TEST_OP_MD_CHECK_DMABUF,
+ IOMMU_TEST_OP_DMABUF_CONTROL,
};
enum {
@@ -80,6 +81,21 @@ enum {
/* Reserved for special pasid replace test */
#define IOMMU_TEST_PASID_RESERVED 1024
+enum {
+ IOMMU_TEST_DMABUF_SET_ERRORS = 1 << 0,
+};
+
+struct iommu_test_dmabuf_stats {
+ __u32 attach_calls;
+ __u32 detach_calls;
+ __u32 pin_calls;
+ __u32 pin_successes;
+ __u32 unpin_calls;
+ __u32 map_calls;
+ __u32 map_successes;
+ __u32 unmap_calls;
+};
+
struct iommu_test_cmd {
__u32 size;
__u32 op;
@@ -201,6 +217,14 @@ struct iommu_test_cmd {
__aligned_u64 length;
__aligned_u64 offset;
} check_dmabuf;
+ struct {
+ __s32 dmabuf_fd;
+ __u32 flags;
+ /* Positive errno, or zero to disable injection. */
+ __s32 pin_errno;
+ __s32 map_errno;
+ struct iommu_test_dmabuf_stats out_stats;
+ } dmabuf_control;
};
__u32 last;
};
diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c
index df1e7d125..ffa26d6e9 100644
--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -1960,6 +1960,10 @@ struct iommufd_test_dma_buf {
size_t length;
unsigned int npages;
bool revoked;
+ int pin_errno;
+ int map_errno;
+ unsigned int attach_count;
+ struct iommu_test_dmabuf_stats stats;
struct page *pages[] __counted_by(npages);
};
@@ -1973,6 +1977,49 @@ static void iommufd_test_dma_buf_release(struct dma_buf *dmabuf)
kfree(priv);
}
+static int iommufd_test_dma_buf_attach(struct dma_buf *dmabuf,
+ struct dma_buf_attachment *attach)
+{
+ struct iommufd_test_dma_buf *priv = dmabuf->priv;
+
+ dma_resv_lock(dmabuf->resv, NULL);
+ priv->attach_count++;
+ priv->stats.attach_calls++;
+ dma_resv_unlock(dmabuf->resv);
+ return 0;
+}
+
+static void iommufd_test_dma_buf_detach(struct dma_buf *dmabuf,
+ struct dma_buf_attachment *attach)
+{
+ struct iommufd_test_dma_buf *priv = dmabuf->priv;
+
+ dma_resv_lock(dmabuf->resv, NULL);
+ priv->attach_count--;
+ priv->stats.detach_calls++;
+ dma_resv_unlock(dmabuf->resv);
+}
+
+static int iommufd_test_dma_buf_pin(struct dma_buf_attachment *attach)
+{
+ struct iommufd_test_dma_buf *priv = attach->dmabuf->priv;
+
+ dma_resv_assert_held(attach->dmabuf->resv);
+ priv->stats.pin_calls++;
+ if (priv->pin_errno)
+ return -priv->pin_errno;
+ priv->stats.pin_successes++;
+ return 0;
+}
+
+static void iommufd_test_dma_buf_unpin(struct dma_buf_attachment *attach)
+{
+ struct iommufd_test_dma_buf *priv = attach->dmabuf->priv;
+
+ dma_resv_assert_held(attach->dmabuf->resv);
+ priv->stats.unpin_calls++;
+}
+
static struct dma_buf_phys_list *
iommufd_dma_pal_map_phys(struct dma_buf_attachment *attachment)
{
@@ -1981,6 +2028,9 @@ iommufd_dma_pal_map_phys(struct dma_buf_attachment *attachment)
dma_resv_assert_held(attachment->dmabuf->resv);
+ priv->stats.map_calls++;
+ if (priv->map_errno)
+ return ERR_PTR(-priv->map_errno);
if (priv->revoked)
return ERR_PTR(-ENODEV);
@@ -1993,12 +2043,17 @@ iommufd_dma_pal_map_phys(struct dma_buf_attachment *attachment)
phys->phys[i].paddr = page_to_phys(priv->pages[i]);
phys->phys[i].len = PAGE_SIZE;
}
+ priv->stats.map_successes++;
return phys;
}
static void iommufd_dma_pal_unmap_phys(struct dma_buf_attachment *attach,
struct dma_buf_phys_list *phys)
{
+ struct iommufd_test_dma_buf *priv = attach->dmabuf->priv;
+
+ dma_resv_assert_held(attach->dmabuf->resv);
+ priv->stats.unmap_calls++;
kfree(phys);
}
@@ -2017,6 +2072,10 @@ static int iommufd_dma_buf_match_mapping(struct dma_buf_match_args *args)
}
static const struct dma_buf_ops iommufd_test_dmabuf_ops = {
+ .attach = iommufd_test_dma_buf_attach,
+ .detach = iommufd_test_dma_buf_detach,
+ .pin = iommufd_test_dma_buf_pin,
+ .unpin = iommufd_test_dma_buf_unpin,
.release = iommufd_test_dma_buf_release,
.match_mapping = iommufd_dma_buf_match_mapping,
};
@@ -2101,6 +2160,56 @@ static int iommufd_test_dmabuf_revoke(struct iommufd_ucmd *ucmd, int fd,
return rc;
}
+static int iommufd_test_dmabuf_control(struct iommufd_ucmd *ucmd)
+{
+ struct iommu_test_cmd *cmd = ucmd->cmd;
+ struct iommufd_test_dma_buf *priv;
+ struct dma_buf *dmabuf;
+ int rc = 0;
+
+ if (cmd->id || cmd->__reserved || cmd->last ||
+ cmd->dmabuf_control.flags & ~IOMMU_TEST_DMABUF_SET_ERRORS)
+ return -EOPNOTSUPP;
+ if (cmd->dmabuf_control.pin_errno < 0 ||
+ cmd->dmabuf_control.pin_errno > MAX_ERRNO ||
+ cmd->dmabuf_control.map_errno < 0 ||
+ cmd->dmabuf_control.map_errno > MAX_ERRNO)
+ return -EINVAL;
+ if (!(cmd->dmabuf_control.flags & IOMMU_TEST_DMABUF_SET_ERRORS) &&
+ (cmd->dmabuf_control.pin_errno || cmd->dmabuf_control.map_errno))
+ return -EINVAL;
+
+ dmabuf = dma_buf_get(cmd->dmabuf_control.dmabuf_fd);
+ if (IS_ERR(dmabuf))
+ return PTR_ERR(dmabuf);
+ if (dmabuf->ops != &iommufd_test_dmabuf_ops) {
+ rc = -EOPNOTSUPP;
+ goto out_put;
+ }
+
+ priv = dmabuf->priv;
+ dma_resv_lock(dmabuf->resv, NULL);
+ if (cmd->dmabuf_control.flags & IOMMU_TEST_DMABUF_SET_ERRORS) {
+ /* Do not change the outcome of an attachment already in use. */
+ if (priv->attach_count) {
+ rc = -EBUSY;
+ goto out_unlock;
+ }
+ priv->pin_errno = cmd->dmabuf_control.pin_errno;
+ priv->map_errno = cmd->dmabuf_control.map_errno;
+ }
+ cmd->dmabuf_control.pin_errno = priv->pin_errno;
+ cmd->dmabuf_control.map_errno = priv->map_errno;
+ cmd->dmabuf_control.out_stats = priv->stats;
+out_unlock:
+ dma_resv_unlock(dmabuf->resv);
+ if (!rc)
+ rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
+out_put:
+ dma_buf_put(dmabuf);
+ return rc;
+}
+
static int iommufd_test_md_check_dmabuf(struct iommufd_ucmd *ucmd,
unsigned int mockpt_id, int dmabuf_fd,
unsigned long iova, size_t length,
@@ -2244,6 +2353,8 @@ int iommufd_test(struct iommufd_ucmd *ucmd)
return iommufd_test_dmabuf_revoke(ucmd,
cmd->dmabuf_revoke.dmabuf_fd,
cmd->dmabuf_revoke.revoked);
+ case IOMMU_TEST_OP_DMABUF_CONTROL:
+ return iommufd_test_dmabuf_control(ucmd);
case IOMMU_TEST_OP_MD_CHECK_DMABUF:
return iommufd_test_md_check_dmabuf(
ucmd, cmd->id, cmd->check_dmabuf.dmabuf_fd,
diff --git a/tools/testing/selftests/iommu/iommufd.c b/tools/testing/selftests/iommu/iommufd.c
index 2673f9f15..771cdf958 100644
--- a/tools/testing/selftests/iommu/iommufd.c
+++ b/tools/testing/selftests/iommu/iommufd.c
@@ -1586,6 +1586,127 @@ TEST_F(iommufd_ioas, dmabuf_simple)
close(dfd);
}
+TEST_F(iommufd_ioas, dmabuf_pin_failure)
+{
+ struct iommu_test_dmabuf_stats stats;
+ __u64 iova;
+ int dfd;
+
+ test_cmd_get_dmabuf(PAGE_SIZE, &dfd);
+ test_cmd_dmabuf_errors(dfd, EBUSY, 0);
+ test_err_ioctl_ioas_map_file(EBUSY, dfd, 0, PAGE_SIZE, &iova);
+ test_cmd_dmabuf_stats(dfd, &stats);
+ EXPECT_EQ(1, stats.attach_calls);
+ EXPECT_EQ(1, stats.detach_calls);
+ EXPECT_EQ(1, stats.pin_calls);
+ EXPECT_EQ(0, stats.pin_successes);
+ EXPECT_EQ(0, stats.unpin_calls);
+ EXPECT_EQ(0, stats.map_calls);
+ EXPECT_EQ(0, stats.map_successes);
+ EXPECT_EQ(0, stats.unmap_calls);
+
+ /* Retry the same buffer after clearing the injected error. */
+ test_cmd_dmabuf_errors(dfd, 0, 0);
+ test_ioctl_ioas_map_file(dfd, 0, PAGE_SIZE, &iova);
+ if (variant->mock_domains)
+ test_cmd_check_dmabuf(self->hwpt_id, dfd, iova, PAGE_SIZE, 0);
+ EXPECT_ERRNO(EBUSY,
+ _test_cmd_dmabuf_control(self->fd, dfd,
+ IOMMU_TEST_DMABUF_SET_ERRORS,
+ EBUSY, 0, NULL));
+ test_ioctl_ioas_unmap(iova, PAGE_SIZE);
+ test_cmd_dmabuf_stats(dfd, &stats);
+ EXPECT_EQ(2, stats.attach_calls);
+ EXPECT_EQ(2, stats.detach_calls);
+ EXPECT_EQ(2, stats.pin_calls);
+ EXPECT_EQ(1, stats.pin_successes);
+ EXPECT_EQ(1, stats.unpin_calls);
+ EXPECT_EQ(1, stats.map_calls);
+ EXPECT_EQ(1, stats.map_successes);
+ EXPECT_EQ(1, stats.unmap_calls);
+ close(dfd);
+}
+
+TEST_F(iommufd_ioas, dmabuf_map_failure)
+{
+ struct iommu_test_dmabuf_stats stats;
+ __u64 iova;
+ int dfd;
+
+ test_cmd_get_dmabuf(PAGE_SIZE, &dfd);
+ test_cmd_dmabuf_errors(dfd, 0, ENOMEM);
+ test_err_ioctl_ioas_map_file(ENOMEM, dfd, 0, PAGE_SIZE, &iova);
+ test_cmd_dmabuf_stats(dfd, &stats);
+ EXPECT_EQ(1, stats.attach_calls);
+ EXPECT_EQ(1, stats.detach_calls);
+ EXPECT_EQ(1, stats.pin_calls);
+ EXPECT_EQ(1, stats.pin_successes);
+ EXPECT_EQ(1, stats.unpin_calls);
+ EXPECT_EQ(1, stats.map_calls);
+ EXPECT_EQ(0, stats.map_successes);
+ EXPECT_EQ(0, stats.unmap_calls);
+
+ test_cmd_dmabuf_errors(dfd, 0, 0);
+ test_ioctl_ioas_map_file(dfd, 0, PAGE_SIZE, &iova);
+ if (variant->mock_domains)
+ test_cmd_check_dmabuf(self->hwpt_id, dfd, iova, PAGE_SIZE, 0);
+ test_ioctl_ioas_unmap(iova, PAGE_SIZE);
+ test_cmd_dmabuf_stats(dfd, &stats);
+ EXPECT_EQ(2, stats.attach_calls);
+ EXPECT_EQ(2, stats.detach_calls);
+ EXPECT_EQ(2, stats.pin_calls);
+ EXPECT_EQ(2, stats.pin_successes);
+ EXPECT_EQ(2, stats.unpin_calls);
+ EXPECT_EQ(2, stats.map_calls);
+ EXPECT_EQ(1, stats.map_successes);
+ EXPECT_EQ(1, stats.unmap_calls);
+ close(dfd);
+}
+
+TEST_F(iommufd_ioas, dmabuf_control_validation)
+{
+ struct iommu_test_cmd cmd = {
+ .size = sizeof(cmd),
+ .op = IOMMU_TEST_OP_DMABUF_CONTROL,
+ };
+ int dfd;
+
+ test_cmd_get_dmabuf(PAGE_SIZE, &dfd);
+ cmd.dmabuf_control.dmabuf_fd = dfd;
+ cmd.__reserved = 1;
+ EXPECT_ERRNO(EOPNOTSUPP, ioctl(self->fd, IOMMU_TEST_CMD, &cmd));
+ cmd.__reserved = 0;
+ cmd.last = 1;
+ EXPECT_ERRNO(EOPNOTSUPP, ioctl(self->fd, IOMMU_TEST_CMD, &cmd));
+ cmd.last = 0;
+ cmd.id = 1;
+ EXPECT_ERRNO(EOPNOTSUPP, ioctl(self->fd, IOMMU_TEST_CMD, &cmd));
+ cmd.id = 0;
+ EXPECT_ERRNO(EOPNOTSUPP,
+ _test_cmd_dmabuf_control(self->fd, dfd, 2, 0, 0, NULL));
+ EXPECT_ERRNO(EINVAL,
+ _test_cmd_dmabuf_control(self->fd, dfd,
+ IOMMU_TEST_DMABUF_SET_ERRORS,
+ -EBUSY, 0, NULL));
+ EXPECT_ERRNO(EINVAL,
+ _test_cmd_dmabuf_control(self->fd, dfd,
+ IOMMU_TEST_DMABUF_SET_ERRORS,
+ 4096, 0, NULL));
+ EXPECT_ERRNO(EINVAL,
+ _test_cmd_dmabuf_control(self->fd, dfd,
+ IOMMU_TEST_DMABUF_SET_ERRORS,
+ 0, -ENOMEM, NULL));
+ EXPECT_ERRNO(EINVAL,
+ _test_cmd_dmabuf_control(self->fd, dfd,
+ IOMMU_TEST_DMABUF_SET_ERRORS,
+ 0, 4096, NULL));
+ EXPECT_ERRNO(EINVAL,
+ _test_cmd_dmabuf_control(self->fd, dfd, 0, EBUSY, 0, NULL));
+ EXPECT_ERRNO(EINVAL,
+ _test_cmd_dmabuf_control(self->fd, dfd, 0, 0, ENOMEM, NULL));
+ close(dfd);
+}
+
TEST_F(iommufd_ioas, dmabuf_multi_page)
{
__u64 iova;
diff --git a/tools/testing/selftests/iommu/iommufd_utils.h b/tools/testing/selftests/iommu/iommufd_utils.h
index 35fd91d35..177a67f0e 100644
--- a/tools/testing/selftests/iommu/iommufd_utils.h
+++ b/tools/testing/selftests/iommu/iommufd_utils.h
@@ -593,6 +593,36 @@ static int _test_cmd_revoke_dmabuf(int fd, int dmabuf_fd, bool revoked)
#define test_cmd_revoke_dmabuf(dmabuf_fd, revoke) \
ASSERT_EQ(0, _test_cmd_revoke_dmabuf(self->fd, dmabuf_fd, revoke))
+static int _test_cmd_dmabuf_control(int fd, int dmabuf_fd, unsigned int flags,
+ int pin_errno, int map_errno,
+ struct iommu_test_dmabuf_stats *stats)
+{
+ struct iommu_test_cmd cmd = {
+ .size = sizeof(cmd),
+ .op = IOMMU_TEST_OP_DMABUF_CONTROL,
+ .dmabuf_control = {
+ .dmabuf_fd = dmabuf_fd,
+ .flags = flags,
+ .pin_errno = pin_errno,
+ .map_errno = map_errno,
+ },
+ };
+ int rc;
+
+ rc = ioctl(fd, IOMMU_TEST_CMD, &cmd);
+ if (!rc && stats)
+ *stats = cmd.dmabuf_control.out_stats;
+ return rc;
+}
+
+#define test_cmd_dmabuf_errors(dfd, pin_error, map_error) \
+ ASSERT_EQ(0, _test_cmd_dmabuf_control( \
+ self->fd, dfd, IOMMU_TEST_DMABUF_SET_ERRORS, pin_error, \
+ map_error, NULL))
+
+#define test_cmd_dmabuf_stats(dfd, stats) \
+ ASSERT_EQ(0, _test_cmd_dmabuf_control(self->fd, dfd, 0, 0, 0, stats))
+
#define test_cmd_check_dmabuf(_hwpt_id, _dmabuf_fd, _iova, _length, _offset) \
({ \
struct iommu_test_cmd check_cmd = { \
--
2.39.5 (Apple Git-154)
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-20 6:38 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-20 6:37 [RFC PATCH 0/6] iommufd: harden DMA-BUF PAL setup and release Karl Mehltretter
2026-10-20 6:37 ` [RFC PATCH 1/6] iommufd: Preserve DMA-buf pin errors Karl Mehltretter
2026-10-20 6:37 ` [RFC PATCH 2/6] iommufd: Publish successful PAL mappings under resv Karl Mehltretter
2026-10-20 6:37 ` [RFC PATCH 3/6] iommufd: Clear the PAL list before dropping resv Karl Mehltretter
2026-10-20 6:37 ` [RFC PATCH 4/6] iommufd: Keep the pages mutex alive until DMA-buf detach Karl Mehltretter
2026-10-20 6:37 ` [RFC PATCH 5/6] iommufd: Unpin the DMA-buf while holding resv Karl Mehltretter
2026-10-20 6:37 ` [RFC PATCH 6/6] iommufd/selftest: Check DMA-buf pin and map failure cleanup Karl Mehltretter
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®