From: Darrick J. Wong <djwong@xxxxxxxxxx> Add the library methods so that fuse servers can manage an in-kernel iomap cache. This enables better performance on small IOs and is required if the filesystem needs synchronization between pagecache writes and writeback. Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- include/fuse_common.h | 9 +++++ include/fuse_kernel.h | 34 +++++++++++++++++++ include/fuse_lowlevel.h | 39 ++++++++++++++++++++++ lib/fuse_lowlevel.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++ lib/fuse_versionscript | 2 + 5 files changed, 166 insertions(+) diff --git a/include/fuse_common.h b/include/fuse_common.h index 98cb8f656efd13..1237cc2656b9c4 100644 --- a/include/fuse_common.h +++ b/include/fuse_common.h @@ -1164,6 +1164,7 @@ int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn); */ #if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 18) #define FUSE_IOMAP_TYPE_PURE_OVERWRITE (0xFFFF) /* use read mapping data */ +#define FUSE_IOMAP_TYPE_NULL (0xFFFE) /* no mapping here */ #define FUSE_IOMAP_TYPE_HOLE 0 /* no blocks allocated, need allocation */ #define FUSE_IOMAP_TYPE_DELALLOC 1 /* delayed allocation blocks */ #define FUSE_IOMAP_TYPE_MAPPED 2 /* blocks allocated at @addr */ @@ -1208,6 +1209,11 @@ struct fuse_iomap { uint32_t dev; /* device cookie */ }; +struct fuse_iomap_inval { + uint64_t offset; /* file offset to invalidate, bytes */ + uint64_t length; /* length to invalidate, bytes */ +}; + /* out of place write extent */ #define FUSE_IOMAP_IOEND_SHARED (1U << 0) /* unwritten extent */ @@ -1258,6 +1264,9 @@ struct fuse_iomap_config{ int64_t s_maxbytes; /* max file size */ }; +/* invalidate to end of file */ +#define FUSE_IOMAP_INVAL_TO_EOF (~0ULL) + #endif /* FUSE_USE_VERSION >= 318 */ /* ----------------------------------------------------------- * diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h index 3c704f03434693..f1a93dbd1ff443 100644 --- a/include/fuse_kernel.h +++ b/include/fuse_kernel.h @@ -243,6 +243,8 @@ * - add FUSE_IOMAP_DIRECTIO/FUSE_ATTR_IOMAP_DIRECTIO for direct I/O support * - add FUSE_IOMAP_FILEIO/FUSE_ATTR_IOMAP_FILEIO for buffered I/O support * - add FUSE_IOMAP_CONFIG so the fuse server can configure more fs geometry + * - add FUSE_NOTIFY_IOMAP_UPSERT and FUSE_NOTIFY_IOMAP_INVAL so fuse servers + * can cache iomappings in the kernel */ #ifndef _LINUX_FUSE_H @@ -699,6 +701,8 @@ enum fuse_notify_code { FUSE_NOTIFY_DELETE = 6, FUSE_NOTIFY_RESEND = 7, FUSE_NOTIFY_INC_EPOCH = 8, + FUSE_NOTIFY_IOMAP_UPSERT = 9, + FUSE_NOTIFY_IOMAP_INVAL = 10, FUSE_NOTIFY_CODE_MAX, }; @@ -1406,4 +1410,34 @@ struct fuse_iomap_config_out { int64_t s_maxbytes; /* max file size */ }; +struct fuse_iomap_upsert_out { + uint64_t nodeid; /* Inode ID */ + uint64_t attr_ino; /* matches fuse_attr:ino */ + + uint64_t read_offset; /* file offset of mapping, bytes */ + uint64_t read_length; /* length of mapping, bytes */ + uint64_t read_addr; /* disk offset of mapping, bytes */ + uint16_t read_type; /* FUSE_IOMAP_TYPE_* */ + uint16_t read_flags; /* FUSE_IOMAP_F_* */ + uint32_t read_dev; /* device cookie */ + + uint64_t write_offset; /* file offset of mapping, bytes */ + uint64_t write_length; /* length of mapping, bytes */ + uint64_t write_addr; /* disk offset of mapping, bytes */ + uint16_t write_type; /* FUSE_IOMAP_TYPE_* */ + uint16_t write_flags; /* FUSE_IOMAP_F_* */ + uint32_t write_dev; /* device cookie * */ +}; + +struct fuse_iomap_inval_out { + uint64_t nodeid; /* Inode ID */ + uint64_t attr_ino; /* matches fuse_attr:ino */ + + uint64_t read_offset; /* range to invalidate read iomaps, bytes */ + uint64_t read_length; /* can be FUSE_IOMAP_INVAL_TO_EOF */ + + uint64_t write_offset; /* range to invalidate write iomaps, bytes */ + uint64_t write_length; /* can be FUSE_IOMAP_INVAL_TO_EOF */ +}; + #endif /* _LINUX_FUSE_H */ diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index fd7df5c2c11e16..f690c62fcdd61c 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -2101,6 +2101,45 @@ int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, * @return positive device id for success, zero for failure */ int fuse_iomap_add_device(struct fuse_session *se, int fd, unsigned int flags); + +/** + * Upsert some file mapping information into the kernel. This is necessary + * for filesystems that require coordination of mapping state changes between + * buffered writes and writeback, and desirable for better performance + * elsewhere. + * + * Added in FUSE protocol version 7.99. If the kernel does not support + * this (or a newer) version, the function will return -ENOSYS and do + * nothing. + * + * @param se the session object + * @param nodeid the inode number + * @param attr_ino inode number as told by fuse_attr::ino + * @param read_iomap mapping information for file reads + * @param write_iomap mapping information for file reads + * @return zero for success, -errno for failure + */ +int fuse_lowlevel_notify_iomap_upsert(struct fuse_session *se, + fuse_ino_t nodeid, uint64_t attr_ino, + const struct fuse_iomap *read_iomap, + const struct fuse_iomap *write_iomap); + +/** + * Invalidate some file mapping information in the kernel. + * + * Added in FUSE protocol version 7.99. If the kernel does not support + * this (or a newer) version, the function will return -ENOSYS and do + * nothing. + * + * @param se the session object + * @param read read mapping range to invalidate + * @param write write mapping range to invalidate + * @return zero for success, -errno for failure + */ +int fuse_lowlevel_notify_iomap_inval(struct fuse_session *se, + fuse_ino_t nodeid, + const struct fuse_iomap_inval *read, + const struct fuse_iomap_inval *write); #endif /* ----------------------------------------------------------- * diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index ed9464d592c8a1..e31ce96593a9b3 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -3349,6 +3349,88 @@ int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, return res; } +int fuse_lowlevel_notify_iomap_upsert(struct fuse_session *se, + fuse_ino_t nodeid, uint64_t attr_ino, + const struct fuse_iomap *read_iomap, + const struct fuse_iomap *write_iomap) +{ + struct fuse_iomap_upsert_out outarg = { + .nodeid = nodeid, + .attr_ino = attr_ino, + }; + struct iovec iov[2]; + + if (!se) + return -EINVAL; + + if (se->conn.proto_minor < 44) + return -ENOSYS; + + if (!read_iomap && !write_iomap) + return 0; + + if (read_iomap) { + outarg.read_offset = read_iomap->offset; + outarg.read_length = read_iomap->length; + outarg.read_addr = read_iomap->addr; + outarg.read_type = read_iomap->type; + outarg.read_flags = read_iomap->flags; + outarg.read_dev = read_iomap->dev; + } else { + outarg.read_type = FUSE_IOMAP_TYPE_NULL; + } + + if (write_iomap) { + outarg.write_offset = write_iomap->offset; + outarg.write_length = write_iomap->length; + outarg.write_addr = write_iomap->addr; + outarg.write_type = write_iomap->type; + outarg.write_flags = write_iomap->flags; + outarg.write_dev = write_iomap->dev; + } else { + outarg.write_type = FUSE_IOMAP_TYPE_NULL; + } + + iov[1].iov_base = &outarg; + iov[1].iov_len = sizeof(outarg); + + return send_notify_iov(se, FUSE_NOTIFY_IOMAP_UPSERT, iov, 2); +} + +int fuse_lowlevel_notify_iomap_inval(struct fuse_session *se, + fuse_ino_t nodeid, + const struct fuse_iomap_inval *read, + const struct fuse_iomap_inval *write) +{ + struct fuse_iomap_inval_out outarg = { + .nodeid = nodeid, + }; + struct iovec iov[2]; + + if (!se) + return -EINVAL; + + if (se->conn.proto_minor < 44) + return -ENOSYS; + + if (!read && !write) + return 0; + + if (read) { + outarg.read_offset = read->offset; + outarg.read_length = read->length; + } + if (write) { + outarg.write_offset = write->offset; + outarg.write_length = write->length; + } + + iov[1].iov_base = &outarg; + iov[1].iov_len = sizeof(outarg); + + return send_notify_iov(se, FUSE_NOTIFY_IOMAP_INVAL, iov, 2); +} + struct fuse_retrieve_req { struct fuse_notify_req nreq; void *cookie; diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript index 9cb46d8a7afdd2..dc9fa2428b5325 100644 --- a/lib/fuse_versionscript +++ b/lib/fuse_versionscript @@ -221,6 +221,8 @@ FUSE_3.18 { fuse_add_direntry_plus_iflags; fuse_discover_iomap; fuse_reply_iomap_config; + fuse_lowlevel_notify_iomap_upsert; + fuse_lowlevel_notify_iomap_inval; } FUSE_3.17; # Local Variables: