[PATCH 13/28] fuse_trace: implement direct IO with iomap

Darrick J. Wong
2025-09-16T00:31:22Z

Add tracepoints for the previous patch.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
fs/fuse/fuse_trace.h | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++
fs/fuse/file_iomap.c | 13 +++++
2 files changed, 157 insertions(+)

diff --git a/fs/fuse/fuse_trace.h b/fs/fuse/fuse_trace.h
index 4fe51be0e65bdc..434d38ce89c428 100644
--- a/fs/fuse/fuse_trace.h
+++ b/fs/fuse/fuse_trace.h
@@ -60,6 +60,7 @@
EM( FUSE_STATX, "FUSE_STATX") \
EM( FUSE_IOMAP_BEGIN, "FUSE_IOMAP_BEGIN") \
EM( FUSE_IOMAP_END, "FUSE_IOMAP_END") \
+ EM( FUSE_IOMAP_IOEND, "FUSE_IOMAP_IOEND") \
EMe(CUSE_INIT, "CUSE_INIT")

/*
@@ -300,6 +301,17 @@ DEFINE_FUSE_BACKING_EVENT(fuse_backing_close);
{ FUSE_IOMAP_TYPE_UNWRITTEN, "unwritten" }, \
{ FUSE_IOMAP_TYPE_INLINE, "inline" }

+#define FUSE_IOMAP_IOEND_STRINGS \
+ { FUSE_IOMAP_IOEND_SHARED, "shared" }, \
+ { FUSE_IOMAP_IOEND_UNWRITTEN, "unwritten" }, \
+ { FUSE_IOMAP_IOEND_BOUNDARY, "boundary" }, \
+ { FUSE_IOMAP_IOEND_DIRECT, "direct" }, \
+ { FUSE_IOMAP_IOEND_APPEND, "append" }
+
+#define IOMAP_DIOEND_STRINGS \
+ { IOMAP_DIO_UNWRITTEN, "unwritten" }, \
+ { IOMAP_DIO_COW, "cow" }
+
TRACE_DEFINE_ENUM(FUSE_I_ADVISE_RDPLUS);
TRACE_DEFINE_ENUM(FUSE_I_INIT_RDPLUS);
TRACE_DEFINE_ENUM(FUSE_I_SIZE_UNSTABLE);
@@ -482,6 +494,65 @@ TRACE_EVENT(fuse_iomap_end_error,
__entry->error)
);

+TRACE_EVENT(fuse_iomap_ioend,
+ TP_PROTO(const struct inode *inode,
+ const struct fuse_iomap_ioend_in *inarg),
+
+ TP_ARGS(inode, inarg),
+
+ TP_STRUCT__entry(
+ FUSE_IO_RANGE_FIELDS()
+ __field(unsigned, ioendflags)
+ __field(int, error)
+ __field(uint64_t, new_addr)
+ ),
+
+ TP_fast_assign(
+ FUSE_INODE_ASSIGN(inode, fi, fm);
+ __entry->offset = inarg->pos;
+ __entry->length = inarg->written;
+ __entry->ioendflags = inarg->ioendflags;
+ __entry->error = inarg->error;
+ __entry->new_addr = inarg->new_addr;
+ ),
+
+ TP_printk(FUSE_IO_RANGE_FMT() " ioendflags (%s) error %d new_addr 0x%llx",
+ FUSE_IO_RANGE_PRINTK_ARGS(),
+ __print_flags(__entry->ioendflags, "|", FUSE_IOMAP_IOEND_STRINGS),
+ __entry->error,
+ __entry->new_addr)
+);
+
+TRACE_EVENT(fuse_iomap_ioend_error,
+ TP_PROTO(const struct inode *inode,
+ const struct fuse_iomap_ioend_in *inarg,
+ int error),
+
+ TP_ARGS(inode, inarg, error),
+
+ TP_STRUCT__entry(
+ FUSE_IO_RANGE_FIELDS()
+ __field(unsigned, ioendflags)
+ __field(int, error)
+ __field(uint64_t, new_addr)
+ ),
+
+ TP_fast_assign(
+ FUSE_INODE_ASSIGN(inode, fi, fm);
+ __entry->offset = inarg->pos;
+ __entry->length = inarg->written;
+ __entry->ioendflags = inarg->ioendflags;
+ __entry->error = error;
+ __entry->new_addr = inarg->new_addr;
+ ),
+
+ TP_printk(FUSE_IO_RANGE_FMT() " ioendflags (%s) error %d new_addr 0x%llx",
+ FUSE_IO_RANGE_PRINTK_ARGS(),
+ __print_flags(__entry->ioendflags, "|", FUSE_IOMAP_IOEND_STRINGS),
+ __entry->error,
+ __entry->new_addr)
+);
+
TRACE_EVENT(fuse_iomap_dev_add,
TP_PROTO(const struct fuse_conn *fc,
const struct fuse_backing_map *map),
@@ -576,6 +647,79 @@ TRACE_EVENT(fuse_iomap_lseek,
__entry->offset,
__entry->whence)
);
+
+DECLARE_EVENT_CLASS(fuse_iomap_file_io_class,
+ TP_PROTO(const struct kiocb *iocb, const struct iov_iter *iter),
+ TP_ARGS(iocb, iter),
+ TP_STRUCT__entry(
+ FUSE_IO_RANGE_FIELDS()
+ ),
+ TP_fast_assign(
+ FUSE_INODE_ASSIGN(file_inode(iocb->ki_filp), fi, fm);
+ __entry->offset = iocb->ki_pos;
+ __entry->length = iov_iter_count(iter);
+ ),
+ TP_printk(FUSE_IO_RANGE_FMT(),
+ FUSE_IO_RANGE_PRINTK_ARGS())
+)
+#define DEFINE_FUSE_IOMAP_FILE_IO_EVENT(name) \
+DEFINE_EVENT(fuse_iomap_file_io_class, name, \
+ TP_PROTO(const struct kiocb *iocb, const struct iov_iter *iter), \
+ TP_ARGS(iocb, iter))
+DEFINE_FUSE_IOMAP_FILE_IO_EVENT(fuse_iomap_direct_read);
+DEFINE_FUSE_IOMAP_FILE_IO_EVENT(fuse_iomap_direct_write);
+
+DECLARE_EVENT_CLASS(fuse_iomap_file_ioend_class,
+ TP_PROTO(const struct kiocb *iocb, const struct iov_iter *iter,
+ ssize_t ret),
+ TP_ARGS(iocb, iter, ret),
+ TP_STRUCT__entry(
+ FUSE_IO_RANGE_FIELDS()
+ __field(ssize_t, ret)
+ ),
+ TP_fast_assign(
+ FUSE_INODE_ASSIGN(file_inode(iocb->ki_filp), fi, fm);
+ __entry->offset = iocb->ki_pos;
+ __entry->length = iov_iter_count(iter);
+ __entry->ret = ret;
+ ),
+ TP_printk(FUSE_IO_RANGE_FMT() " ret 0x%zx",
+ FUSE_IO_RANGE_PRINTK_ARGS(),
+ __entry->ret)
+)
+#define DEFINE_FUSE_IOMAP_FILE_IOEND_EVENT(name) \
+DEFINE_EVENT(fuse_iomap_file_ioend_class, name, \
+ TP_PROTO(const struct kiocb *iocb, const struct iov_iter *iter, \
+ ssize_t ret), \
+ TP_ARGS(iocb, iter, ret))
+DEFINE_FUSE_IOMAP_FILE_IOEND_EVENT(fuse_iomap_direct_read_end);
+DEFINE_FUSE_IOMAP_FILE_IOEND_EVENT(fuse_iomap_direct_write_end);
+
+TRACE_EVENT(fuse_iomap_dio_write_end_io,
+ TP_PROTO(const struct inode *inode, loff_t pos, ssize_t written,
+ int error, unsigned flags),
+
+ TP_ARGS(inode, pos, written, error, flags),
+
+ TP_STRUCT__entry(
+ FUSE_IO_RANGE_FIELDS()
+ __field(unsigned, dioendflags)
+ __field(int, error)
+ ),
+
+ TP_fast_assign(
+ FUSE_INODE_ASSIGN(inode, fi, fm);
+ __entry->offset = pos;
+ __entry->length = written;
+ __entry->dioendflags = flags;
+ __entry->error = error;
+ ),
+
+ TP_printk(FUSE_IO_RANGE_FMT() " dioendflags (%s) error %d",
+ FUSE_IO_RANGE_PRINTK_ARGS(),
+ __print_flags(__entry->dioendflags, "|", IOMAP_DIOEND_STRINGS),
+ __entry->error)
+);
#endif /* CONFIG_FUSE_IOMAP */

#endif /* _TRACE_FUSE_H */
diff --git a/fs/fuse/file_iomap.c b/fs/fuse/file_iomap.c
index 84eb1fe4fcde49..54e09f60980ef1 100644
--- a/fs/fuse/file_iomap.c
+++ b/fs/fuse/file_iomap.c
@@ -618,6 +618,8 @@ static int fuse_iomap_ioend(struct inode *inode, loff_t pos, size_t written,
if (fuse_ioend_is_append(fi, pos, written))
inarg.ioendflags |= FUSE_IOMAP_IOEND_APPEND;

+ trace_fuse_iomap_ioend(inode, &inarg);
+
if (fuse_should_send_iomap_ioend(fm, &inarg)) {
FUSE_ARGS(args);
int err;
@@ -640,6 +642,8 @@ static int fuse_iomap_ioend(struct inode *inode, loff_t pos, size_t written,
case 0:
break;
default:
+ trace_fuse_iomap_ioend_error(inode, &inarg, err);
+
/*
* If the write IO failed, return the failure code to
* the caller no matter what happens with the ioend.
@@ -888,6 +892,8 @@ ssize_t fuse_iomap_direct_read(struct kiocb *iocb, struct iov_iter *to)

ASSERT(fuse_inode_has_iomap(inode));

+ trace_fuse_iomap_direct_read(iocb, to);
+
if (!iov_iter_count(to))
return 0; /* skip atime */

@@ -899,6 +905,7 @@ ssize_t fuse_iomap_direct_read(struct kiocb *iocb, struct iov_iter *to)
ret = iomap_dio_rw(iocb, to, &fuse_iomap_ops, NULL, 0, NULL, 0);
inode_unlock_shared(inode);

+ trace_fuse_iomap_direct_read_end(iocb, to, ret);
return ret;
}

@@ -915,6 +922,9 @@ static int fuse_iomap_dio_write_end_io(struct kiocb *iocb, ssize_t written,

ASSERT(fuse_inode_has_iomap(inode));

+ trace_fuse_iomap_dio_write_end_io(inode, iocb->ki_pos, written, error,
+ dioflags);
+
if (dioflags & IOMAP_DIO_COW)
ioendflags |= FUSE_IOMAP_IOEND_SHARED;
if (dioflags & IOMAP_DIO_UNWRITTEN)
@@ -946,6 +956,8 @@ ssize_t fuse_iomap_direct_write(struct kiocb *iocb, struct iov_iter *from)

ASSERT(fuse_inode_has_iomap(inode));

+ trace_fuse_iomap_direct_write(iocb, from);
+
if (!count)
return 0;

@@ -982,5 +994,6 @@ ssize_t fuse_iomap_direct_write(struct kiocb *iocb, struct iov_iter *from)
out_unlock:
inode_unlock(inode);
out_dsync:
+ trace_fuse_iomap_direct_write_end(iocb, from, ret);
return ret;
}