From: Miklos Szeredi <mszeredi@redhat.com>
To: fuse-devel@lists.linux.dev
Cc: John Groves <john@groves.net>,
Amir Goldstein <amir73il@gmail.com>,
"Darrick J . Wong" <djwong@kernel.org>,
Vishal Verma <vishal.l.verma@intel.com>,
Dave Jiang <dave.jiang@intel.com>,
Alison Schofield <alison.schofield@intel.com>,
nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org
Subject: [PATCH v2 7/8] fuse: add extent map I/O support
Date: Thu, 1 Oct 2026 17:07:25 +0200 [thread overview]
Message-ID: <20261001150935.655979-8-mszeredi@redhat.com> (raw)
In-Reply-To: <20261001150935.655979-1-mszeredi@redhat.com>
Wire up read, write, splice and mmap operations for extent-mapped
files through the iomap/DAX infrastructure.
When a passthrough file is opened with an EXTMAP backing, I/O is
dispatched to the dax devices referenced by the extent map.
If an address is not mapped, EIO is returned on the I/O operation.
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
---
fs/fuse/backing.c | 15 +++++
fs/fuse/ext_map.c | 149 ++++++++++++++++++++++++++++++++++++++++++
fs/fuse/fuse_i.h | 4 ++
fs/fuse/notify.c | 3 +
fs/fuse/passthrough.c | 29 ++++++--
5 files changed, 194 insertions(+), 6 deletions(-)
diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c
index e954391d696d..2708f140b94f 100644
--- a/fs/fuse/backing.c
+++ b/fs/fuse/backing.c
@@ -278,6 +278,21 @@ int fuse_backing_close(struct fuse_conn *fc, int backing_id)
return err;
}
+bool fuse_backing_is_dax(struct fuse_backing *fb)
+{
+ switch (fb->type) {
+ case FUSE_BACKING_PATH:
+ return false;
+ case FUSE_BACKING_DAXDEV:
+ return true;
+ case FUSE_BACKING_EXTMAP:
+ return fuse_ext_map_is_dax(fb);
+ default:
+ WARN_ON(1);
+ return false;
+ }
+}
+
struct fuse_backing *fuse_backing_lookup(struct fuse_conn *fc, u64 backing_id)
{
struct fuse_backing *fb;
diff --git a/fs/fuse/ext_map.c b/fs/fuse/ext_map.c
index ab01d678d08e..61cb22e821b6 100644
--- a/fs/fuse/ext_map.c
+++ b/fs/fuse/ext_map.c
@@ -3,6 +3,8 @@
#include "fuse_i.h"
#include <linux/rbtree.h>
#include <linux/pagemap.h>
+#include <linux/iomap.h>
+#include <linux/dax.h>
struct fuse_iext {
struct rb_node rb;
@@ -22,6 +24,24 @@ void fuse_ext_map_destroy(struct rb_root *extents)
}
}
+static struct fuse_iext *fuse_find_extent(struct rb_root *extents, u64 offset)
+{
+ struct rb_node *node = extents->rb_node;
+
+ while (node) {
+ struct fuse_iext *fie = rb_entry(node, typeof(*fie), rb);
+
+ if (offset < fie->start)
+ node = node->rb_left;
+ else if (offset >= fie->end)
+ node = node->rb_right;
+ else
+ return fie;
+ }
+
+ return NULL;
+}
+
static int fuse_add_extent(struct fuse_conn *fc, struct rb_root *extents,
struct fuse_extent *ext)
{
@@ -76,6 +96,135 @@ static int fuse_add_extent(struct fuse_conn *fc, struct rb_root *extents,
return 0;
}
+static int fuse_ext_map_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
+ unsigned int flags, struct iomap *iomap, struct iomap *srcmap)
+{
+ struct fuse_backing *fb = fuse_inode_backing(get_fuse_inode(inode));
+ struct fuse_iext *fie;
+ loff_t ext_len;
+
+ if (!fb || fb->type != FUSE_BACKING_EXTMAP)
+ return fuse_EIO("missing or wrong type backing");
+
+ fie = fuse_find_extent(&fb->extents, offset);
+ if (!fie)
+ return fuse_EIO("missing mapping");
+
+ if (WARN_ON(fie->backing->type != FUSE_BACKING_DAXDEV))
+ return fuse_EIO("wrong type backing for extent");
+
+ if (fie->backing->dax_error) {
+ fuse_make_bad(inode);
+ return fuse_EIO("dax error");
+ }
+
+ ext_len = fie->end - fie->start;
+
+ iomap->offset = fie->start;
+ iomap->addr = fie->backing_offset;
+ iomap->length = ext_len;
+ iomap->dax_dev = fie->backing->dax_dev;
+ iomap->type = IOMAP_MAPPED;
+ iomap->flags = 0;
+
+ return 0;
+}
+
+static const struct iomap_ops fuse_ext_map_iomap_ops = {
+ .iomap_begin = fuse_ext_map_iomap_begin,
+};
+
+static vm_fault_t fuse_ext_map_huge_fault(struct vm_fault *vmf, unsigned int order)
+{
+ struct inode *inode = file_inode(vmf->vma->vm_file);
+ bool write_fault = (vmf->flags & FAULT_FLAG_WRITE) && (vmf->vma->vm_flags & VM_SHARED);
+ vm_fault_t ret;
+ unsigned long pfn;
+
+ if (WARN_ON_ONCE(!IS_DAX(inode)))
+ return VM_FAULT_SIGBUS;
+
+ if (write_fault) {
+ sb_start_pagefault(inode->i_sb);
+ file_update_time(vmf->vma->vm_file);
+ }
+
+ filemap_invalidate_lock_shared(inode->i_mapping);
+
+ ret = dax_iomap_fault(vmf, order, &pfn, NULL, &fuse_ext_map_iomap_ops);
+ if (ret & VM_FAULT_NEEDDSYNC)
+ ret = dax_finish_sync_fault(vmf, order, pfn);
+
+ filemap_invalidate_unlock_shared(inode->i_mapping);
+
+ if (write_fault)
+ sb_end_pagefault(inode->i_sb);
+
+ return ret;
+}
+
+static vm_fault_t fuse_ext_map_fault(struct vm_fault *vmf)
+{
+ return fuse_ext_map_huge_fault(vmf, 0);
+}
+
+static const struct vm_operations_struct fuse_ext_map_vm_ops = {
+ .fault = fuse_ext_map_fault,
+ .huge_fault = fuse_ext_map_huge_fault,
+ .page_mkwrite = fuse_ext_map_fault,
+ .pfn_mkwrite = fuse_ext_map_fault,
+};
+
+static void fuse_rw_clamp(struct kiocb *iocb, struct iov_iter *ubuf)
+{
+ struct inode *inode = iocb->ki_filp->f_mapping->host;
+ loff_t i_size = i_size_read(inode);
+ loff_t max_count = iocb->ki_pos >= i_size ? 0 : i_size - iocb->ki_pos;
+
+ if (iov_iter_count(ubuf) > max_count)
+ iov_iter_truncate(ubuf, max_count);
+}
+
+ssize_t fuse_ext_map_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+ ssize_t res;
+
+ fuse_rw_clamp(iocb, to);
+
+ if (!iov_iter_count(to))
+ return 0;
+
+ res = dax_iomap_rw(iocb, to, &fuse_ext_map_iomap_ops);
+
+ file_accessed(iocb->ki_filp);
+ return res;
+}
+
+ssize_t fuse_ext_map_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+ ssize_t res;
+
+ fuse_rw_clamp(iocb, from);
+
+ res = generic_write_checks(iocb, from);
+ if (res <= 0)
+ return res;
+
+ res = kiocb_modified(iocb);
+ if (res)
+ return res;
+
+ return dax_iomap_rw(iocb, from, &fuse_ext_map_iomap_ops);
+}
+
+int fuse_ext_map_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ file_accessed(file);
+ vma->vm_ops = &fuse_ext_map_vm_ops;
+ vm_flags_set(vma, VM_HUGEPAGE);
+ return 0;
+}
+
bool fuse_ext_map_is_dax(struct fuse_backing *fb)
{
struct fuse_iext *fie;
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 5a20f3863ee2..7248588b45c4 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1316,6 +1316,7 @@ static inline void fuse_backing_put(struct fuse_backing *fb)
struct fuse_backing *fuse_backing_lookup(struct fuse_conn *fc, u64 backing_id);
int fuse_backing_add_64(struct fuse_conn *fc, struct fuse_backing *fb);
+bool fuse_backing_is_dax(struct fuse_backing *fb);
void fuse_backing_files_init(struct fuse_conn *fc);
void fuse_backing_files_init_64(struct fuse_conn *fc);
void fuse_backing_files_free(struct fuse_conn *fc);
@@ -1369,6 +1370,9 @@ extern void fuse_sysctl_unregister(void);
/* ext_map.c */
void fuse_ext_map_destroy(struct rb_root *extents);
+ssize_t fuse_ext_map_write_iter(struct kiocb *iocb, struct iov_iter *from);
+ssize_t fuse_ext_map_read_iter(struct kiocb *iocb, struct iov_iter *to);
+int fuse_ext_map_mmap(struct file *file, struct vm_area_struct *vma);
bool fuse_ext_map_is_dax(struct fuse_backing *fb);
int fuse_ext_map_populate(struct fuse_conn *fc, struct fuse_notify_backing_map_out *arg,
struct fuse_extent *ext);
diff --git a/fs/fuse/notify.c b/fs/fuse/notify.c
index 7c427f88bbc5..3bfedd0cdbdd 100644
--- a/fs/fuse/notify.c
+++ b/fs/fuse/notify.c
@@ -448,6 +448,9 @@ static int fuse_notify_map(struct fuse_conn *fc, unsigned int size,
if (err)
return err;
+ if (!fc->backing_id_64)
+ return -EINVAL;
+
if (outarg.num_extents > FUSE_MAX_EXTENTS)
return -EINVAL;
diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c
index e9ab1aea34e2..040817ad76e9 100644
--- a/fs/fuse/passthrough.c
+++ b/fs/fuse/passthrough.c
@@ -35,7 +35,6 @@ ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *iter)
struct fuse_file *ff = file->private_data;
struct file *backing_file = fuse_file_passthrough(ff);
size_t count = iov_iter_count(iter);
- ssize_t ret;
struct backing_file_ctx ctx = {
.cred = ff->cred,
.accessed = fuse_file_accessed,
@@ -48,10 +47,10 @@ ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *iter)
if (!count)
return 0;
- ret = backing_file_read_iter(backing_file, iter, iocb, iocb->ki_flags,
- &ctx);
+ if (!backing_file)
+ return fuse_ext_map_read_iter(iocb, iter);
- return ret;
+ return backing_file_read_iter(backing_file, iter, iocb, iocb->ki_flags, &ctx);
}
ssize_t fuse_passthrough_write_iter(struct kiocb *iocb,
@@ -74,10 +73,13 @@ ssize_t fuse_passthrough_write_iter(struct kiocb *iocb,
if (!count)
return 0;
- inode_lock(inode);
+ guard(rwsem_write)(&inode->i_rwsem);
+
+ if (!backing_file)
+ return fuse_ext_map_write_iter(iocb, iter);
+
ret = backing_file_write_iter(backing_file, iter, iocb, iocb->ki_flags,
&ctx);
- inode_unlock(inode);
return ret;
}
@@ -98,6 +100,9 @@ ssize_t fuse_passthrough_splice_read(struct file *in, loff_t *ppos,
pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", __func__,
backing_file, *ppos, len, flags);
+ if (!backing_file)
+ return copy_splice_read(in, ppos, pipe, len, flags);
+
init_sync_kiocb(&iocb, in);
iocb.ki_pos = *ppos;
ret = backing_file_splice_read(backing_file, &iocb, pipe, len, flags, &ctx);
@@ -123,6 +128,9 @@ ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe,
pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", __func__,
backing_file, *ppos, len, flags);
+ if (!backing_file)
+ return iter_file_splice_write(pipe, out, ppos, len, flags);
+
inode_lock(inode);
init_sync_kiocb(&iocb, out);
iocb.ki_pos = *ppos;
@@ -145,6 +153,9 @@ ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma)
pr_debug("%s: backing_file=0x%p, start=%lu, end=%lu\n", __func__,
backing_file, vma->vm_start, vma->vm_end);
+ if (!backing_file)
+ return fuse_ext_map_mmap(file, vma);
+
return backing_file_mmap(backing_file, vma, &ctx);
}
@@ -156,6 +167,12 @@ int fuse_passthrough_open(struct file *file, struct fuse_backing *fb)
struct fuse_file *ff = file->private_data;
struct file *backing_file;
+ if (fb->type == FUSE_BACKING_EXTMAP) {
+ if (fuse_backing_is_dax(fb) != !!IS_DAX(file_inode(file)))
+ return fuse_EIO("dax mode mismatch");
+ return 0;
+ }
+
if (fb->type != FUSE_BACKING_PATH)
return fuse_EIO("invalid backing type");
--
2.54.0
next prev parent reply other threads:[~2026-10-01 15:09 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-01 15:07 [PATCH v2 0/8] fuse: DAX device based extent maps (famfs) Miklos Szeredi
2026-10-01 15:07 ` [PATCH v2 1/8] dax: replace exported dax_dev_get() with non-allocating dax_dev_find() Miklos Szeredi
2026-10-01 15:07 ` [PATCH v2 2/8] fuse: add helpers for EIO return value with kernel message Miklos Szeredi
2026-10-01 15:18 ` sashiko-bot
2026-10-01 16:32 ` Amir Goldstein
2026-10-05 9:46 ` Miklos Szeredi
2026-10-01 15:07 ` [PATCH v2 3/8] fuse: support 64 bit, server allocated backing ID Miklos Szeredi
2026-10-01 15:26 ` sashiko-bot
2026-10-01 17:07 ` Amir Goldstein
2026-10-01 18:58 ` Amir Goldstein
2026-10-05 13:33 ` Miklos Szeredi
2026-10-06 21:24 ` Amir Goldstein
2026-10-07 12:46 ` Miklos Szeredi
2026-10-01 15:07 ` [PATCH v2 4/8] fuse: support opening 64 bit " Miklos Szeredi
2026-10-01 15:22 ` sashiko-bot
2026-10-01 17:09 ` Amir Goldstein
2026-10-01 15:07 ` [PATCH v2 5/8] fuse: add support for opening dax device as backing Miklos Szeredi
2026-10-01 15:30 ` sashiko-bot
2026-10-01 16:07 ` Amir Goldstein
2026-10-01 15:07 ` [PATCH v2 6/8] fuse: add extent map data structure Miklos Szeredi
2026-10-01 15:24 ` sashiko-bot
2026-10-01 15:07 ` Miklos Szeredi [this message]
2026-10-01 15:27 ` [PATCH v2 7/8] fuse: add extent map I/O support sashiko-bot
2026-10-01 16:11 ` Amir Goldstein
2026-10-01 15:07 ` [PATCH v2 8/8] fuse: add support for striped backing Miklos Szeredi
2026-10-05 23:27 ` [PATCH v2 0/8] fuse: DAX device based extent maps (famfs) John Groves
2026-10-06 9:48 ` Miklos Szeredi
2026-10-08 23:00 ` John Groves
2026-10-09 10:39 ` Miklos Szeredi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261001150935.655979-8-mszeredi@redhat.com \
--to=mszeredi@redhat.com \
--cc=alison.schofield@intel.com \
--cc=amir73il@gmail.com \
--cc=dave.jiang@intel.com \
--cc=djwong@kernel.org \
--cc=fuse-devel@lists.linux.dev \
--cc=john@groves.net \
--cc=linux-cxl@vger.kernel.org \
--cc=nvdimm@lists.linux.dev \
--cc=vishal.l.verma@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.