[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <176169813659.1427432.5276282398065113594.stgit@frogsfrogsfrogs>
Date: Tue, 28 Oct 2025 18:01:08 -0700
From: "Darrick J. Wong" <djwong@...nel.org>
To: djwong@...nel.org, bschubert@....com
Cc: linux-ext4@...r.kernel.org, linux-fsdevel@...r.kernel.org,
bernd@...ernd.com, miklos@...redi.hu, joannelkoong@...il.com, neal@...pa.dev
Subject: [PATCH 07/22] libfuse: add iomap ioend low level handler
From: Darrick J. Wong <djwong@...nel.org>
Teach the low level library about the iomap ioend handler, which gets
called by the kernel when we finish a file write that isn't a pure
overwrite operation.
Signed-off-by: "Darrick J. Wong" <djwong@...nel.org>
---
include/fuse_common.h | 13 +++++++++++++
include/fuse_kernel.h | 11 +++++++++++
include/fuse_lowlevel.h | 20 ++++++++++++++++++++
lib/fuse_lowlevel.c | 23 +++++++++++++++++++++++
4 files changed, 67 insertions(+)
diff --git a/include/fuse_common.h b/include/fuse_common.h
index 12b951039f0a67..c75428dae64e2f 100644
--- a/include/fuse_common.h
+++ b/include/fuse_common.h
@@ -1208,6 +1208,19 @@ static inline bool fuse_iomap_need_write_allocate(unsigned int opflags,
!(opflags & FUSE_IOMAP_OP_ZERO);
}
+/* out of place write extent */
+#define FUSE_IOMAP_IOEND_SHARED (1U << 0)
+/* unwritten extent */
+#define FUSE_IOMAP_IOEND_UNWRITTEN (1U << 1)
+/* don't merge into previous ioend */
+#define FUSE_IOMAP_IOEND_BOUNDARY (1U << 2)
+/* is direct I/O */
+#define FUSE_IOMAP_IOEND_DIRECT (1U << 3)
+/* is append ioend */
+#define FUSE_IOMAP_IOEND_APPEND (1U << 4)
+/* is pagecache writeback */
+#define FUSE_IOMAP_IOEND_WRITEBACK (1U << 5)
+
/* ----------------------------------------------------------- *
* Compatibility stuff *
* ----------------------------------------------------------- */
diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h
index 3857259e27f9c1..378019cc15cfd3 100644
--- a/include/fuse_kernel.h
+++ b/include/fuse_kernel.h
@@ -668,6 +668,7 @@ enum fuse_opcode {
FUSE_STATX = 52,
FUSE_COPY_FILE_RANGE_64 = 53,
+ FUSE_IOMAP_IOEND = 4093,
FUSE_IOMAP_BEGIN = 4094,
FUSE_IOMAP_END = 4095,
@@ -1352,4 +1353,14 @@ struct fuse_iomap_end_in {
struct fuse_iomap_io map;
};
+struct fuse_iomap_ioend_in {
+ uint32_t ioendflags; /* FUSE_IOMAP_IOEND_* */
+ int32_t error; /* negative errno or 0 */
+ uint64_t attr_ino; /* matches fuse_attr:ino */
+ uint64_t pos; /* file position, in bytes */
+ uint64_t new_addr; /* disk offset of new mapping, in bytes */
+ uint32_t written; /* bytes processed */
+ uint32_t reserved1; /* zero */
+};
+
#endif /* _LINUX_FUSE_H */
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index dcacde79e78b1a..bef2e709d559b0 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -1379,6 +1379,26 @@ struct fuse_lowlevel_ops {
void (*iomap_end) (fuse_req_t req, fuse_ino_t nodeid, uint64_t attr_ino,
off_t pos, uint64_t count, uint32_t opflags,
ssize_t written, const struct fuse_file_iomap *iomap);
+
+ /**
+ * Complete an iomap IO operation
+ *
+ * Valid replies:
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param nodeid the inode number
+ * @param attr_ino inode number as told by fuse_attr::ino
+ * @param pos position in file, in bytes
+ * @param written number of bytes processed, or a negative errno
+ * @param ioendflags mask of FUSE_IOMAP_IOEND_ flags specifying operation
+ * @param error errno code of what went wrong
+ * @param new_addr disk address of new mapping, in bytes
+ */
+ void (*iomap_ioend) (fuse_req_t req, fuse_ino_t nodeid,
+ uint64_t attr_ino, off_t pos, size_t written,
+ uint32_t ioendflags, int error,
+ uint64_t new_addr);
};
/**
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 570253b9dc74b6..3cfabdaed6439d 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -2696,6 +2696,27 @@ static void do_iomap_end(fuse_req_t req, const fuse_ino_t nodeid,
_do_iomap_end(req, nodeid, inarg, NULL);
}
+static void _do_iomap_ioend(fuse_req_t req, const fuse_ino_t nodeid,
+ const void *op_in, const void *in_payload)
+{
+ const struct fuse_iomap_ioend_in *arg = op_in;
+ (void)in_payload;
+ (void)nodeid;
+
+ if (req->se->op.iomap_ioend)
+ req->se->op.iomap_ioend(req, nodeid, arg->attr_ino, arg->pos,
+ arg->written, arg->ioendflags,
+ arg->error, arg->new_addr);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_iomap_ioend(fuse_req_t req, const fuse_ino_t nodeid,
+ const void *inarg)
+{
+ _do_iomap_ioend(req, nodeid, inarg, NULL);
+}
+
static bool want_flags_valid(uint64_t capable, uint64_t want)
{
uint64_t unknown_flags = want & (~capable);
@@ -3605,6 +3626,7 @@ static struct {
[FUSE_STATX] = { do_statx, "STATX" },
[FUSE_IOMAP_BEGIN] = { do_iomap_begin, "IOMAP_BEGIN" },
[FUSE_IOMAP_END] = { do_iomap_end, "IOMAP_END" },
+ [FUSE_IOMAP_IOEND] = { do_iomap_ioend, "IOMAP_IOEND" },
[CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
};
@@ -3663,6 +3685,7 @@ static struct {
[FUSE_STATX] = { _do_statx, "STATX" },
[FUSE_IOMAP_BEGIN] = { _do_iomap_begin, "IOMAP_BEGIN" },
[FUSE_IOMAP_END] = { _do_iomap_end, "IOMAP_END" },
+ [FUSE_IOMAP_IOEND] = { _do_iomap_ioend, "IOMAP_IOEND" },
[CUSE_INIT] = { _cuse_lowlevel_init, "CUSE_INIT" },
};
Powered by blists - more mailing lists