* [RFC 1/4] block: factor out bdev_init_dma_buf_io_ctx()
2026-10-16 9:19 [RFC 0/4] fs: support dma-buf backed direct I/O Anuj Gupta
@ 2026-10-16 9:19 ` Anuj Gupta
2026-10-16 9:23 ` Christoph Hellwig
2026-10-16 9:19 ` [RFC 2/4] block: refuse a dma-buf bio for a disk the buffer was not registered on Anuj Gupta
` (4 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Anuj Gupta @ 2026-10-16 9:19 UTC (permalink / raw)
To: asml.silence, hch, djwong, cem, brauner, axboe
Cc: linux-block, linux-xfs, linux-fsdevel, joshi.k, anuj20.g,
nj.shetty
From: Nitesh Shetty <nj.shetty@samsung.com>
Factor the checks and driver setup into an exported helper that takes
an explicit block device, so filesystems can initialize a dma-buf I/O
context for the device backing a regular file. The block-device file
operation becomes a wrapper using file_bdev(), so its behavior is
unchanged.
Signed-off-by: Nitesh Shetty <nj.shetty@samsung.com>
---
block/fops.c | 12 +++++++++---
include/linux/blkdev.h | 3 +++
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/block/fops.c b/block/fops.c
index 49ae0302f123..18a755f6e0b0 100644
--- a/block/fops.c
+++ b/block/fops.c
@@ -934,10 +934,9 @@ static int blkdev_mmap_prepare(struct vm_area_desc *desc)
return generic_file_mmap_prepare(desc);
}
-static int blkdev_init_dma_buf_io_ctx(struct file *file,
- struct dma_buf_io_ctx *ctx)
+int bdev_init_dma_buf_io_ctx(struct file *file, struct block_device *bdev,
+ struct dma_buf_io_ctx *ctx)
{
- struct block_device *bdev = file_bdev(file);
struct gendisk *disk = bdev->bd_disk;
if (!(file->f_flags & O_DIRECT))
@@ -948,6 +947,13 @@ static int blkdev_init_dma_buf_io_ctx(struct file *file,
return -EOPNOTSUPP;
return disk->fops->init_dma_buf_io_ctx(bdev, ctx);
}
+EXPORT_SYMBOL_GPL(bdev_init_dma_buf_io_ctx);
+
+static int blkdev_init_dma_buf_io_ctx(struct file *file,
+ struct dma_buf_io_ctx *ctx)
+{
+ return bdev_init_dma_buf_io_ctx(file, file_bdev(file), ctx);
+}
const struct file_operations def_blk_fops = {
.open = blkdev_open,
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 2d555a992e75..a0abdf94e7d6 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1609,6 +1609,9 @@ struct block_device_operations {
int (*alternative_gpt_sector)(struct gendisk *disk, sector_t *sector);
};
+int bdev_init_dma_buf_io_ctx(struct file *file, struct block_device *bdev,
+ struct dma_buf_io_ctx *ctx);
+
#ifdef CONFIG_COMPAT
extern int blkdev_compat_ptr_ioctl(struct block_device *, blk_mode_t,
unsigned int, unsigned long);
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [RFC 1/4] block: factor out bdev_init_dma_buf_io_ctx()
2026-10-16 9:19 ` [RFC 1/4] block: factor out bdev_init_dma_buf_io_ctx() Anuj Gupta
@ 2026-10-16 9:23 ` Christoph Hellwig
0 siblings, 0 replies; 15+ messages in thread
From: Christoph Hellwig @ 2026-10-16 9:23 UTC (permalink / raw)
To: Anuj Gupta
Cc: asml.silence, hch, djwong, cem, brauner, axboe, linux-block,
linux-xfs, linux-fsdevel, joshi.k, anuj20.g, nj.shetty
On Fri, Oct 16, 2026 at 02:49:11PM +0530, Anuj Gupta wrote:
> From: Nitesh Shetty <nj.shetty@samsung.com>
>
> Factor the checks and driver setup into an exported helper that takes
> an explicit block device, so filesystems can initialize a dma-buf I/O
> context for the device backing a regular file. The block-device file
> operation becomes a wrapper using file_bdev(), so its behavior is
> unchanged.
Please move hte helper out of block/fops.c, as that only is for
the block device file operations.
^ permalink raw reply [flat|nested] 15+ messages in thread
* [RFC 2/4] block: refuse a dma-buf bio for a disk the buffer was not registered on
2026-10-16 9:19 [RFC 0/4] fs: support dma-buf backed direct I/O Anuj Gupta
2026-10-16 9:19 ` [RFC 1/4] block: factor out bdev_init_dma_buf_io_ctx() Anuj Gupta
@ 2026-10-16 9:19 ` Anuj Gupta
2026-10-16 9:24 ` Christoph Hellwig
2026-10-16 9:19 ` [RFC 3/4] iomap: add dma-buf direct I/O guards and registration helper Anuj Gupta
` (3 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Anuj Gupta @ 2026-10-16 9:19 UTC (permalink / raw)
To: asml.silence, hch, djwong, cem, brauner, axboe
Cc: linux-block, linux-xfs, linux-fsdevel, joshi.k, anuj20.g,
nj.shetty
From: Nitesh Shetty <nj.shetty@samsung.com>
A registered dma-buf map belongs to the device it was mapped for.
The device backing a file can change after registration. Record
the registered disk's dev_t at init and refuse a bio that targets
a different one.
Signed-off-by: Nitesh Shetty <nj.shetty@samsung.com>
---
block/bio.c | 15 +++++++++++++++
block/fops.c | 7 ++++++-
include/linux/dma-buf-io.h | 2 ++
3 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/block/bio.c b/block/bio.c
index 795b88e4a994..b9e5c73cd3d1 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -19,6 +19,7 @@
#include <linux/blk-crypto.h>
#include <linux/xarray.h>
#include <linux/kmemleak.h>
+#include <linux/dma-buf-io.h>
#include <trace/events/block.h>
#include "blk.h"
@@ -1269,6 +1270,17 @@ static inline bool bio_iov_bvec_aligned(const struct bio *bio,
}
#endif
+/*
+ * A dma-buf map is only valid for the disk it was registered on, but
+ * the device backing a file can change after the registration.
+ */
+static bool bio_dmabuf_map_valid(const struct bio *bio,
+ const struct iov_iter *iter)
+{
+ return iter->dmabuf_map->ctx->registered_devt ==
+ disk_devt(bio->bi_bdev->bd_disk);
+}
+
/**
* bio_iov_iter_get_pages - add user or kernel pages to a bio
* @bio: bio to add pages to
@@ -1300,6 +1312,9 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,
if (WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED)))
return -EIO;
+ if (iov_iter_is_dmabuf_map(iter) && !bio_dmabuf_map_valid(bio, iter))
+ return -EXDEV;
+
if (bio_iov_iter_set(bio, iter)) {
if (iov_iter_is_bvec(iter) &&
!bio_iov_bvec_aligned(bio, mem_align_mask))
diff --git a/block/fops.c b/block/fops.c
index 18a755f6e0b0..b36dde10a4ac 100644
--- a/block/fops.c
+++ b/block/fops.c
@@ -19,6 +19,7 @@
#include <linux/iomap.h>
#include <linux/module.h>
#include <linux/io_uring/cmd.h>
+#include <linux/dma-buf-io.h>
#include "blk.h"
static inline struct inode *bdev_file_inode(struct file *file)
@@ -938,6 +939,7 @@ int bdev_init_dma_buf_io_ctx(struct file *file, struct block_device *bdev,
struct dma_buf_io_ctx *ctx)
{
struct gendisk *disk = bdev->bd_disk;
+ int ret;
if (!(file->f_flags & O_DIRECT))
return -EINVAL;
@@ -945,7 +947,10 @@ int bdev_init_dma_buf_io_ctx(struct file *file, struct block_device *bdev,
return -EOPNOTSUPP;
if (bdev_has_integrity_csum(bdev))
return -EOPNOTSUPP;
- return disk->fops->init_dma_buf_io_ctx(bdev, ctx);
+ ret = disk->fops->init_dma_buf_io_ctx(bdev, ctx);
+ if (!ret)
+ ctx->registered_devt = disk_devt(disk);
+ return ret;
}
EXPORT_SYMBOL_GPL(bdev_init_dma_buf_io_ctx);
diff --git a/include/linux/dma-buf-io.h b/include/linux/dma-buf-io.h
index b9bbeca1744f..d42a79f2ac9b 100644
--- a/include/linux/dma-buf-io.h
+++ b/include/linux/dma-buf-io.h
@@ -50,6 +50,8 @@ struct dma_buf_io_ctx {
void *dev_priv;
const struct dma_buf_io_ops *dev_ops;
+
+ dev_t registered_devt;
};
int dma_buf_io_ctx_create(struct file *file,
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [RFC 2/4] block: refuse a dma-buf bio for a disk the buffer was not registered on
2026-10-16 9:19 ` [RFC 2/4] block: refuse a dma-buf bio for a disk the buffer was not registered on Anuj Gupta
@ 2026-10-16 9:24 ` Christoph Hellwig
0 siblings, 0 replies; 15+ messages in thread
From: Christoph Hellwig @ 2026-10-16 9:24 UTC (permalink / raw)
To: Anuj Gupta
Cc: asml.silence, hch, djwong, cem, brauner, axboe, linux-block,
linux-xfs, linux-fsdevel, joshi.k, anuj20.g, nj.shetty
On Fri, Oct 16, 2026 at 02:49:12PM +0530, Anuj Gupta wrote:
> From: Nitesh Shetty <nj.shetty@samsung.com>
>
> A registered dma-buf map belongs to the device it was mapped for.
> The device backing a file can change after registration. Record
> the registered disk's dev_t at init and refuse a bio that targets
> a different one.
Can you explain a bit better what this is trying to solve?
^ permalink raw reply [flat|nested] 15+ messages in thread
* [RFC 3/4] iomap: add dma-buf direct I/O guards and registration helper
2026-10-16 9:19 [RFC 0/4] fs: support dma-buf backed direct I/O Anuj Gupta
2026-10-16 9:19 ` [RFC 1/4] block: factor out bdev_init_dma_buf_io_ctx() Anuj Gupta
2026-10-16 9:19 ` [RFC 2/4] block: refuse a dma-buf bio for a disk the buffer was not registered on Anuj Gupta
@ 2026-10-16 9:19 ` Anuj Gupta
2026-10-16 9:26 ` Christoph Hellwig
2026-10-16 9:19 ` [RFC 4/4] xfs: support dma-buf registered buffers for direct I/O Anuj Gupta
` (2 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Anuj Gupta @ 2026-10-16 9:19 UTC (permalink / raw)
To: asml.silence, hch, djwong, cem, brauner, axboe
Cc: linux-block, linux-xfs, linux-fsdevel, joshi.k, anuj20.g,
nj.shetty
From: Nitesh Shetty <nj.shetty@samsung.com>
Direct I/O copies data from the CPU in three places, zero-filling
holes, inline data, and bounce pages. A dma-buf iterator has no
CPU-visible pages, so refuse it.
Reads that encounter a hole or unwritten extent therefore return
-EOPNOTSUPP for the entire operation, even if I/O for an earlier mapped
extent was already submitted.
Also add iomap_file_init_dma_buf_io_ctx(), so filesystems can register
without open-coding the DAX check. The filesystem picks the device,
iomap does the rest.
Signed-off-by: Nitesh Shetty <nj.shetty@samsung.com>
Signed-off-by: Anuj Gupta <anuj20.g@samsung.com>
---
fs/iomap/direct-io.c | 31 ++++++++++++++++++++++++++++++-
include/linux/iomap.h | 3 +++
2 files changed, 33 insertions(+), 1 deletion(-)
diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c
index 8b4039d16ce8..fc40f4367ee7 100644
--- a/fs/iomap/direct-io.c
+++ b/fs/iomap/direct-io.c
@@ -427,6 +427,10 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio)
unsigned int alignment;
ssize_t ret = 0;
+ if (iov_iter_is_dmabuf_map(dio->submit.iter) &&
+ (iomap->flags & IOMAP_F_INTEGRITY))
+ return -EOPNOTSUPP;
+
/*
* File systems that write out of place and always allocate new blocks
* need each bio to be block aligned as that's the unit of allocation.
@@ -595,7 +599,13 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio)
static int iomap_dio_hole_iter(struct iomap_iter *iter, struct iomap_dio *dio)
{
- loff_t length = iov_iter_zero(iomap_length(iter), dio->submit.iter);
+ loff_t length;
+
+ /* a device mapping cannot be zeroed from the CPU */
+ if (iov_iter_is_dmabuf_map(dio->submit.iter))
+ return -EOPNOTSUPP;
+
+ length = iov_iter_zero(iomap_length(iter), dio->submit.iter);
dio->size += length;
if (!length)
@@ -612,6 +622,10 @@ static int iomap_dio_inline_iter(struct iomap_iter *iomi, struct iomap_dio *dio)
loff_t pos = iomi->pos;
u64 copied;
+ /* inline data is copied from the CPU, which a device mapping bypasses */
+ if (iov_iter_is_dmabuf_map(iter))
+ return -EOPNOTSUPP;
+
if (WARN_ON_ONCE(!inline_data))
return -EIO;
@@ -710,6 +724,10 @@ __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
if (!iomi.len)
return NULL;
+ /* bouncing copies the data from the CPU, which a device mapping bypasses */
+ if (iov_iter_is_dmabuf_map(iter) && (dio_flags & IOMAP_DIO_BOUNCE))
+ return ERR_PTR(-EOPNOTSUPP);
+
dio = kmalloc_obj(*dio);
if (!dio)
return ERR_PTR(-ENOMEM);
@@ -915,6 +933,17 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
}
EXPORT_SYMBOL_GPL(iomap_dio_rw);
+int iomap_file_init_dma_buf_io_ctx(struct file *file, struct block_device *bdev,
+ struct dma_buf_io_ctx *ctx)
+{
+ struct inode *inode = file_inode(file);
+
+ if (IS_DAX(inode))
+ return -EOPNOTSUPP;
+ return bdev_init_dma_buf_io_ctx(file, bdev, ctx);
+}
+EXPORT_SYMBOL_GPL(iomap_file_init_dma_buf_io_ctx);
+
struct iomap_dio_simple {
struct kiocb *iocb;
size_t size;
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index bc7ae6327dbf..ce97df81c644 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -682,6 +682,9 @@ struct iomap_dio *__iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
ssize_t iomap_dio_complete(struct iomap_dio *dio);
void iomap_dio_bio_end_io(struct bio *bio);
+int iomap_file_init_dma_buf_io_ctx(struct file *file, struct block_device *bdev,
+ struct dma_buf_io_ctx *ctx);
+
/*
* Fast path for small, block-aligned direct I/Os that map to a single
* contiguous on-disk extent.
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [RFC 3/4] iomap: add dma-buf direct I/O guards and registration helper
2026-10-16 9:19 ` [RFC 3/4] iomap: add dma-buf direct I/O guards and registration helper Anuj Gupta
@ 2026-10-16 9:26 ` Christoph Hellwig
0 siblings, 0 replies; 15+ messages in thread
From: Christoph Hellwig @ 2026-10-16 9:26 UTC (permalink / raw)
To: Anuj Gupta
Cc: asml.silence, hch, djwong, cem, brauner, axboe, linux-block,
linux-xfs, linux-fsdevel, joshi.k, anuj20.g, nj.shetty
On Fri, Oct 16, 2026 at 02:49:13PM +0530, Anuj Gupta wrote:
> From: Nitesh Shetty <nj.shetty@samsung.com>
>
> Direct I/O copies data from the CPU in three places, zero-filling
> holes, inline data, and bounce pages. A dma-buf iterator has no
> CPU-visible pages, so refuse it.
>
> Reads that encounter a hole or unwritten extent therefore return
> -EOPNOTSUPP for the entire operation, even if I/O for an earlier mapped
> extent was already submitted.
>
> Also add iomap_file_init_dma_buf_io_ctx(), so filesystems can register
> without open-coding the DAX check. The filesystem picks the device,
> iomap does the rest.
>
> Signed-off-by: Nitesh Shetty <nj.shetty@samsung.com>
> Signed-off-by: Anuj Gupta <anuj20.g@samsung.com>
> ---
> fs/iomap/direct-io.c | 31 ++++++++++++++++++++++++++++++-
> include/linux/iomap.h | 3 +++
> 2 files changed, 33 insertions(+), 1 deletion(-)
>
> diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c
> index 8b4039d16ce8..fc40f4367ee7 100644
> --- a/fs/iomap/direct-io.c
> +++ b/fs/iomap/direct-io.c
> @@ -427,6 +427,10 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio)
> unsigned int alignment;
> ssize_t ret = 0;
>
> + if (iov_iter_is_dmabuf_map(dio->submit.iter) &&
> + (iomap->flags & IOMAP_F_INTEGRITY))
> + return -EOPNOTSUPP;
> +
> /*
> * File systems that write out of place and always allocate new blocks
> * need each bio to be block aligned as that's the unit of allocation.
> @@ -595,7 +599,13 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio)
>
> static int iomap_dio_hole_iter(struct iomap_iter *iter, struct iomap_dio *dio)
> {
> - loff_t length = iov_iter_zero(iomap_length(iter), dio->submit.iter);
> + loff_t length;
> +
> + /* a device mapping cannot be zeroed from the CPU */
> + if (iov_iter_is_dmabuf_map(dio->submit.iter))
> + return -EOPNOTSUPP;
> +
> + length = iov_iter_zero(iomap_length(iter), dio->submit.iter);
>
> dio->size += length;
> if (!length)
> @@ -612,6 +622,10 @@ static int iomap_dio_inline_iter(struct iomap_iter *iomi, struct iomap_dio *dio)
> loff_t pos = iomi->pos;
> u64 copied;
>
> + /* inline data is copied from the CPU, which a device mapping bypasses */
Overly lone line here.
> + /* bouncing copies the data from the CPU, which a device mapping bypasses */
.. and here. Note that could still copy to/from the device memory in
either case, but that's probably rather pointless at least for actual
device dmabufs. It might be useful for udmabuf or other kernel
allocations, though.
^ permalink raw reply [flat|nested] 15+ messages in thread
* [RFC 4/4] xfs: support dma-buf registered buffers for direct I/O
2026-10-16 9:19 [RFC 0/4] fs: support dma-buf backed direct I/O Anuj Gupta
` (2 preceding siblings ...)
2026-10-16 9:19 ` [RFC 3/4] iomap: add dma-buf direct I/O guards and registration helper Anuj Gupta
@ 2026-10-16 9:19 ` Anuj Gupta
2026-10-16 9:28 ` Christoph Hellwig
2026-10-17 22:54 ` [RFC 0/4] fs: support dma-buf backed " shinichiro.kawasaki
2026-10-18 22:18 ` Pavel Begunkov
5 siblings, 1 reply; 15+ messages in thread
From: Anuj Gupta @ 2026-10-16 9:19 UTC (permalink / raw)
To: asml.silence, hch, djwong, cem, brauner, axboe
Cc: linux-block, linux-xfs, linux-fsdevel, joshi.k, anuj20.g,
nj.shetty
From: Nitesh Shetty <nj.shetty@samsung.com>
Add the init_dma_buf_io_ctx fop for XFS files.
XFS picks the device and calls the iomap layer with
the relevant device.
Refuse the iterator from every CPU-copy path: DAX, buffered
fallback from a -ENOTBLK direct write, and read/writes
without O_DIRECT.
If a file is switched between the data and realtime devices
after registration, the block layer cross-device check refuses
the I/O.
Signed-off-by: Nitesh Shetty <nj.shetty@samsung.com>
---
fs/xfs/xfs_file.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 426a67b813a7..f97d5acfaf68 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -340,6 +340,11 @@ xfs_file_read_iter(
if (xfs_is_shutdown(mp))
return -EIO;
+ /* a device mapping can only be read by a direct I/O bio */
+ if (iov_iter_is_dmabuf_map(to) &&
+ (IS_DAX(inode) || !(iocb->ki_flags & IOCB_DIRECT)))
+ return -EOPNOTSUPP;
+
if (IS_DAX(inode))
ret = xfs_file_dax_read(iocb, to);
else if (iocb->ki_flags & IOCB_DIRECT)
@@ -1196,6 +1201,11 @@ xfs_file_write_iter(
return ret;
}
+ /* a device mapping can only be written by a direct I/O bio */
+ if (iov_iter_is_dmabuf_map(from) &&
+ (IS_DAX(inode) || !(iocb->ki_flags & IOCB_DIRECT)))
+ return -EOPNOTSUPP;
+
if (IS_DAX(inode))
return xfs_file_dax_write(iocb, from);
@@ -1209,6 +1219,9 @@ xfs_file_write_iter(
ret = xfs_file_dio_write(iocb, from);
if (ret != -ENOTBLK)
return ret;
+ /* the page cache cannot copy from a device mapping */
+ if (iov_iter_is_dmabuf_map(from))
+ return -EOPNOTSUPP;
}
if (xfs_is_zoned_inode(ip))
@@ -2173,10 +2186,27 @@ xfs_file_mmap_prepare(
return 0;
}
+/*
+ * A realtime file's data lives on the realtime device, anything else on the
+ * data device. If a file is switched between them after registration, the
+ * block layer cross-device check refuses the I/O.
+ */
+static int
+xfs_file_init_dma_buf_io_ctx(
+ struct file *file,
+ struct dma_buf_io_ctx *ctx)
+{
+ struct xfs_inode *ip = XFS_I(file_inode(file));
+
+ return iomap_file_init_dma_buf_io_ctx(file,
+ xfs_inode_buftarg(ip)->bt_bdev, ctx);
+}
+
const struct file_operations xfs_file_operations = {
.llseek = xfs_file_llseek,
.read_iter = xfs_file_read_iter,
.write_iter = xfs_file_write_iter,
+ .init_dma_buf_io_ctx = xfs_file_init_dma_buf_io_ctx,
.splice_read = xfs_file_splice_read,
.splice_write = iter_file_splice_write,
.iopoll = iocb_bio_iopoll,
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [RFC 4/4] xfs: support dma-buf registered buffers for direct I/O
2026-10-16 9:19 ` [RFC 4/4] xfs: support dma-buf registered buffers for direct I/O Anuj Gupta
@ 2026-10-16 9:28 ` Christoph Hellwig
2026-10-17 18:19 ` Kanchan Joshi
` (2 more replies)
0 siblings, 3 replies; 15+ messages in thread
From: Christoph Hellwig @ 2026-10-16 9:28 UTC (permalink / raw)
To: Anuj Gupta
Cc: asml.silence, hch, djwong, cem, brauner, axboe, linux-block,
linux-xfs, linux-fsdevel, joshi.k, anuj20.g, nj.shetty
On Fri, Oct 16, 2026 at 02:49:14PM +0530, Anuj Gupta wrote:
> From: Nitesh Shetty <nj.shetty@samsung.com>
>
> Add the init_dma_buf_io_ctx fop for XFS files.
> XFS picks the device and calls the iomap layer with
> the relevant device.
>
> Refuse the iterator from every CPU-copy path: DAX, buffered
> fallback from a -ENOTBLK direct write, and read/writes
> without O_DIRECT.
>
> If a file is switched between the data and realtime devices
> after registration, the block layer cross-device check refuses
> the I/O.
>
> Signed-off-by: Nitesh Shetty <nj.shetty@samsung.com>
> ---
> fs/xfs/xfs_file.c | 30 ++++++++++++++++++++++++++++++
> 1 file changed, 30 insertions(+)
>
> diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
> index 426a67b813a7..f97d5acfaf68 100644
> --- a/fs/xfs/xfs_file.c
> +++ b/fs/xfs/xfs_file.c
> @@ -340,6 +340,11 @@ xfs_file_read_iter(
> if (xfs_is_shutdown(mp))
> return -EIO;
>
> + /* a device mapping can only be read by a direct I/O bio */
> + if (iov_iter_is_dmabuf_map(to) &&
> + (IS_DAX(inode) || !(iocb->ki_flags & IOCB_DIRECT)))
> + return -EOPNOTSUPP;
Maybe move this into the actual methods for dax/buffered instead
to avoid extra conditionals? Same for the write side.
>
> @@ -1209,6 +1219,9 @@ xfs_file_write_iter(
> ret = xfs_file_dio_write(iocb, from);
> if (ret != -ENOTBLK)
> return ret;
> + /* the page cache cannot copy from a device mapping */
> + if (iov_iter_is_dmabuf_map(from))
> + return -EOPNOTSUPP;
I suspect these random fallbacks are going to cause trouble, but
I'm not really sure what to do about it either.
> +/*
> + * A realtime file's data lives on the realtime device, anything else on the
> + * data device. If a file is switched between them after registration, the
> + * block layer cross-device check refuses the I/O.
> + */
> +static int
> +xfs_file_init_dma_buf_io_ctx(
> + struct file *file,
> + struct dma_buf_io_ctx *ctx)
> +{
> + struct xfs_inode *ip = XFS_I(file_inode(file));
> +
> + return iomap_file_init_dma_buf_io_ctx(file,
> + xfs_inode_buftarg(ip)->bt_bdev, ctx);
> +}
Ah, this is where the previous cross check comes from. What about
simply disallowing toggling the RT flag with outstanding reservations
instead?
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [RFC 4/4] xfs: support dma-buf registered buffers for direct I/O
2026-10-16 9:28 ` Christoph Hellwig
@ 2026-10-17 18:19 ` Kanchan Joshi
2026-10-18 22:28 ` Pavel Begunkov
2026-10-19 10:29 ` Anuj gupta
2 siblings, 0 replies; 15+ messages in thread
From: Kanchan Joshi @ 2026-10-17 18:19 UTC (permalink / raw)
To: Christoph Hellwig, Anuj Gupta
Cc: asml.silence, djwong, cem, brauner, axboe, linux-block, linux-xfs,
linux-fsdevel, anuj20.g, nj.shetty
On 10/16/2026 2:58 PM, Christoph Hellwig wrote:
>> +/*
>> + * A realtime file's data lives on the realtime device, anything else on the
>> + * data device. If a file is switched between them after registration, the
>> + * block layer cross-device check refuses the I/O.
>> + */
>> +static int
>> +xfs_file_init_dma_buf_io_ctx(
>> + struct file *file,
>> + struct dma_buf_io_ctx *ctx)
>> +{
>> + struct xfs_inode *ip = XFS_I(file_inode(file));
>> +
>> + return iomap_file_init_dma_buf_io_ctx(file,
>> + xfs_inode_buftarg(ip)->bt_bdev, ctx);
>> +}
> Ah, this is where the previous cross check comes from. What about
> simply disallowing toggling the RT flag with outstanding reservations
> instead?
So, instead of patch 2, this is about setting a new in-core inode flag
here iff the registration was successful. And checking this new flag to
refuse toggling RT?
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [RFC 4/4] xfs: support dma-buf registered buffers for direct I/O
2026-10-16 9:28 ` Christoph Hellwig
2026-10-17 18:19 ` Kanchan Joshi
@ 2026-10-18 22:28 ` Pavel Begunkov
2026-10-19 10:29 ` Anuj gupta
2 siblings, 0 replies; 15+ messages in thread
From: Pavel Begunkov @ 2026-10-18 22:28 UTC (permalink / raw)
To: Christoph Hellwig, Anuj Gupta
Cc: djwong, cem, brauner, axboe, linux-block, linux-xfs,
linux-fsdevel, joshi.k, anuj20.g, nj.shetty
On 10/16/26 10:28, Christoph Hellwig wrote:
> On Fri, Oct 16, 2026 at 02:49:14PM +0530, Anuj Gupta wrote:
>> From: Nitesh Shetty <nj.shetty@samsung.com>
>>
>> Add the init_dma_buf_io_ctx fop for XFS files.
>> XFS picks the device and calls the iomap layer with
>> the relevant device.
>>
>> Refuse the iterator from every CPU-copy path: DAX, buffered
>> fallback from a -ENOTBLK direct write, and read/writes
>> without O_DIRECT.
>>
>> If a file is switched between the data and realtime devices
>> after registration, the block layer cross-device check refuses
>> the I/O.
>>
>> Signed-off-by: Nitesh Shetty <nj.shetty@samsung.com>
>> ---
>> fs/xfs/xfs_file.c | 30 ++++++++++++++++++++++++++++++
>> 1 file changed, 30 insertions(+)
>>
>> diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
>> index 426a67b813a7..f97d5acfaf68 100644
>> --- a/fs/xfs/xfs_file.c
>> +++ b/fs/xfs/xfs_file.c
>> @@ -340,6 +340,11 @@ xfs_file_read_iter(
>> if (xfs_is_shutdown(mp))
>> return -EIO;
>>
>> + /* a device mapping can only be read by a direct I/O bio */
>> + if (iov_iter_is_dmabuf_map(to) &&
>> + (IS_DAX(inode) || !(iocb->ki_flags & IOCB_DIRECT)))
>> + return -EOPNOTSUPP;
>
> Maybe move this into the actual methods for dax/buffered instead
> to avoid extra conditionals? Same for the write side.
>
>>
>> @@ -1209,6 +1219,9 @@ xfs_file_write_iter(
>> ret = xfs_file_dio_write(iocb, from);
>> if (ret != -ENOTBLK)
>> return ret;
>> + /* the page cache cannot copy from a device mapping */
>> + if (iov_iter_is_dmabuf_map(from))
>> + return -EOPNOTSUPP;
>
> I suspect these random fallbacks are going to cause trouble, but
> I'm not really sure what to do about it either.
Folks did mention it to me before. For a slow path we can try
do something like amdxdna_cbuf_clear() does:
dma_buf_vmap(dbuf, &vmap);
if (!vmap.vaddr)
return -EFAULT;
memset(vmap.vaddr, 0, dbuf->size);
dma_buf_vunmap(dbuf, &vmap);
But I can already imagine it triggering some interesting
behaviour like buffer migration, which will be too penalising
for things like clear on a hole. And we'll need to be careful
about deadlocking here.
--
Pavel Begunkov
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [RFC 4/4] xfs: support dma-buf registered buffers for direct I/O
2026-10-16 9:28 ` Christoph Hellwig
2026-10-17 18:19 ` Kanchan Joshi
2026-10-18 22:28 ` Pavel Begunkov
@ 2026-10-19 10:29 ` Anuj gupta
2 siblings, 0 replies; 15+ messages in thread
From: Anuj gupta @ 2026-10-19 10:29 UTC (permalink / raw)
To: Christoph Hellwig
Cc: asml.silence, djwong, cem, brauner, axboe, linux-block, linux-xfs,
linux-fsdevel, joshi.k, anuj20.g, nj.shetty
> > +/*
> > + * A realtime file's data lives on the realtime device, anything else on the
> > + * data device. If a file is switched between them after registration, the
> > + * block layer cross-device check refuses the I/O.
> > + */
> > +static int
> > +xfs_file_init_dma_buf_io_ctx(
> > + struct file *file,
> > + struct dma_buf_io_ctx *ctx)
> > +{
> > + struct xfs_inode *ip = XFS_I(file_inode(file));
> > +
> > + return iomap_file_init_dma_buf_io_ctx(file,
> > + xfs_inode_buftarg(ip)->bt_bdev, ctx);
> > +}
>
> Ah, this is where the previous cross check comes from. What about
> simply disallowing toggling the RT flag with outstanding reservations
> instead?
That works for the current exact-file scheme, but not with Pavel's
shared-context proposal[1], since other files can use the buffer without
registering it. There we can validate the context tag against the
inode's current backing device per I/O.
[1] https://lore.kernel.org/linux-xfs/4649a20f-aba9-418e-987f-66f21ab24410@gmail.com/
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [RFC 0/4] fs: support dma-buf backed direct I/O
2026-10-16 9:19 [RFC 0/4] fs: support dma-buf backed direct I/O Anuj Gupta
` (3 preceding siblings ...)
2026-10-16 9:19 ` [RFC 4/4] xfs: support dma-buf registered buffers for direct I/O Anuj Gupta
@ 2026-10-17 22:54 ` shinichiro.kawasaki
2026-10-18 13:32 ` Shin'ichiro Kawasaki
2026-10-18 22:18 ` Pavel Begunkov
5 siblings, 1 reply; 15+ messages in thread
From: shinichiro.kawasaki @ 2026-10-17 22:54 UTC (permalink / raw)
To: linux-block; +Cc: blktests-ci-bot
[-- Attachment #1: Type: text/plain, Size: 330 bytes --]
Dear patch submitter,
Blktests CI has tested the following submission:
Status: FAILURE
Name: [RFC,0/4] fs: support dma-buf backed direct I/O
Patchwork: https://patchwork.kernel.org/series/1187147/&state=*
Run record: https://github.com/linux-blktests/linux-block/actions/runs/38999893251
Failed test cases: md/001
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [RFC 0/4] fs: support dma-buf backed direct I/O
2026-10-16 9:19 [RFC 0/4] fs: support dma-buf backed direct I/O Anuj Gupta
` (4 preceding siblings ...)
2026-10-17 22:54 ` [RFC 0/4] fs: support dma-buf backed " shinichiro.kawasaki
@ 2026-10-18 22:18 ` Pavel Begunkov
5 siblings, 0 replies; 15+ messages in thread
From: Pavel Begunkov @ 2026-10-18 22:18 UTC (permalink / raw)
To: Anuj Gupta, hch, djwong, cem, brauner, axboe
Cc: linux-block, linux-xfs, linux-fsdevel, joshi.k, anuj20.g,
nj.shetty
On 10/16/26 10:19, Anuj Gupta wrote:
> This series lets an application register a dma-buf against a file,
> so reads and writes go directly to the device as dma-buf backed
> bios with no per-command DMA mapping.
Great to see work done on the filesystem side!
For filesystems to be useful, we'll first need to make a registered
buffer work with all files of the fs and not only the one we used for
the registration. I think about something like below. I'll clean it up
and send out later.
commit 11e25bf0dbce227f87b81cb9436ad347de0deb06
Author: Pavel Begunkov <asml.silence@gmail.com>
Date: Sun Oct 18 23:09:44 2026 +0100
dma-buf-io: allow compatible file to reuse the context
Currently, we limit the use of a dma-buf I/O context to the file it was
created from. It might be fine for block device files but won't work
well with file systems as we need to support multiple files. Let the
importing subsystem to set an opaque handle to file and indirectly to
the dma-buf I/O context, and use it to allow only compatible files.
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
diff --git a/block/fops.c b/block/fops.c
index 3fcf33bba935..c62f9f6cc4e3 100644
--- a/block/fops.c
+++ b/block/fops.c
@@ -685,6 +685,7 @@ static int blkdev_open(struct inode *inode, struct file *filp)
ret = bdev_open(bdev, mode, filp->private_data, NULL, filp);
if (ret)
blkdev_put_no_open(bdev);
+ filp->f_dma_io_tag = (unsigned long)bdev;
return ret;
}
diff --git a/drivers/dma-buf/dma-buf-io.c b/drivers/dma-buf/dma-buf-io.c
index f724936e223b..a99b93cd4be4 100644
--- a/drivers/dma-buf/dma-buf-io.c
+++ b/drivers/dma-buf/dma-buf-io.c
@@ -166,12 +166,15 @@ int dma_buf_io_ctx_create(struct file *file,
if (!file->f_op->init_dma_buf_io_ctx)
return -EOPNOTSUPP;
+ if (WARN_ON(!file->f_dma_io_tag))
+ return -EFAULT;
ctx = kzalloc_obj(*ctx);
if (!ctx)
return -ENOMEM;
ctx->dir = dir;
ctx->dmabuf = dmabuf;
+ ctx->importer_tag = file->f_dma_io_tag;
get_dma_buf(dmabuf);
ret = file->f_op->init_dma_buf_io_ctx(file, ctx);
diff --git a/include/linux/dma-buf-io.h b/include/linux/dma-buf-io.h
index d9f27650a851..c1d72024dd11 100644
--- a/include/linux/dma-buf-io.h
+++ b/include/linux/dma-buf-io.h
@@ -48,6 +48,15 @@ struct dma_buf_io_ctx {
enum dma_data_direction dir;
bool is_dead;
+ /*
+ * A tag uniqiue to the importing subsystem, which used for filtering
+ * out files incompatible with the dma-buf I/O context by matching it
+ * against struct file::f_dma_io_tag. It encodes both the set of
+ * devices it has been registered for and the subsystem specific
+ * map / context structures representation.
+ */
+ unsigned long importer_tag;
+
void *dev_priv;
const struct dma_buf_io_ops *dev_ops;
};
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 0d826777681d..8949ac27308c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1292,6 +1292,12 @@ struct file {
};
file_ref_t f_ref;
/* --- cacheline 3 boundary (192 bytes) --- */
+
+ /*
+ * A tag representing the underlying subsystem for dma-buf I/O.
+ * See struct dma_buf_io_ctx::importer_tag.
+ */
+ unsigned long f_dma_io_tag;
} __randomize_layout
__attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
diff --git a/io_uring/dma-buf.c b/io_uring/dma-buf.c
index 132e3d0d7023..d8771dced990 100644
--- a/io_uring/dma-buf.c
+++ b/io_uring/dma-buf.c
@@ -4,7 +4,6 @@ static inline void io_release_dmabuf(void *priv)
{
struct io_buf_dma *bd = priv;
- fput(bd->target_file);
dma_buf_io_ctx_release(bd->ctx);
kfree(bd);
}
@@ -52,13 +51,13 @@ int __io_register_dmabuf(struct io_ring_ctx *ctx,
if (ret)
goto err;
- bd->target_file = target_file;
imu->len = dmabuf->size;
imu->release = io_release_dmabuf;
imu->priv = bd;
imu->flags = IO_REGBUF_F_DMABUF;
imu->dir = IO_BUF_DEST | IO_BUF_SOURCE;
dma_buf_put(dmabuf);
+ fput(target_file);
return 0;
err:
kfree(bd);
diff --git a/io_uring/dma-buf.h b/io_uring/dma-buf.h
index a876a9e5e272..3b812758fbf7 100644
--- a/io_uring/dma-buf.h
+++ b/io_uring/dma-buf.h
@@ -9,7 +9,6 @@
struct io_buf_dma {
struct dma_buf_io_ctx *ctx;
- struct file *target_file;
};
int __io_register_dmabuf(struct io_ring_ctx *ctx,
@@ -57,7 +56,7 @@ static inline int io_import_dmabuf_map(struct io_kiocb *req,
if (!IS_ENABLED(CONFIG_DMA_SHARED_BUFFER))
return -EOPNOTSUPP;
- if (req->file != bd->target_file)
+ if (req->file->f_dma_io_tag != bd->ctx->importer_tag)
return -EBADF;
req->flags |= REQ_F_DMABUF;
iov_iter_dmabuf_map(iter, ddir, NULL, offset, len);
--
Pavel Begunkov
^ permalink raw reply related [flat|nested] 15+ messages in thread