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 | 12 ++++++++ include/fuse_kernel.h | 26 +++++++++++++++++ include/fuse_lowlevel.h | 41 ++++++++++++++++++++++++++ lib/fuse_lowlevel.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++ lib/fuse_versionscript | 2 + 5 files changed, 154 insertions(+) diff --git a/include/fuse_common.h b/include/fuse_common.h index 19770262c4b518..a1c5199f2cb4ee 100644 --- a/include/fuse_common.h +++ b/include/fuse_common.h @@ -1168,6 +1168,10 @@ int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn); /* fuse-specific mapping type indicating that writes use the read mapping */ #define FUSE_IOMAP_TYPE_PURE_OVERWRITE (255) +/* fuse-specific mapping type saying the server has populated the cache */ +#define FUSE_IOMAP_TYPE_RETRY_CACHE (254) +/* do not upsert this mapping */ +#define FUSE_IOMAP_TYPE_NOCACHE (253) #define FUSE_IOMAP_DEV_NULL (0U) /* null device cookie */ @@ -1273,6 +1277,14 @@ struct fuse_iomap_config{ int64_t s_maxbytes; /* max file size */ }; +/* invalidate to end of file */ +#define FUSE_IOMAP_INVAL_TO_EOF (~0ULL) + +struct fuse_iomap_inval { + uint64_t offset; /* file offset to invalidate, bytes */ + uint64_t length; /* length to invalidate, bytes */ +}; + /* ----------------------------------------------------------- * * Compatibility stuff * * ----------------------------------------------------------- */ diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h index fcf02c9371ba3a..0c30aebaf95c32 100644 --- a/include/fuse_kernel.h +++ b/include/fuse_kernel.h @@ -243,6 +243,8 @@ * - add FUSE_IOMAP_CONFIG so the fuse server can configure more fs geometry * - add FUSE_NOTIFY_IOMAP_DEV_INVAL to invalidate iomap bdev ranges * - add FUSE_ATTR_ATOMIC for single-fsblock atomic write support + * - add FUSE_NOTIFY_IOMAP_UPSERT and FUSE_NOTIFY_IOMAP_INVAL so fuse servers + * can cache iomappings in the kernel */ #ifndef _LINUX_FUSE_H @@ -696,6 +698,8 @@ enum fuse_notify_code { FUSE_NOTIFY_RESEND = 7, FUSE_NOTIFY_INC_EPOCH = 8, FUSE_NOTIFY_IOMAP_DEV_INVAL = 9, + FUSE_NOTIFY_IOMAP_UPSERT = 10, + FUSE_NOTIFY_IOMAP_INVAL = 11, FUSE_NOTIFY_CODE_MAX, }; @@ -1401,4 +1405,26 @@ struct fuse_iomap_dev_inval { uint64_t offset; /* range to invalidate pagecache, bytes */ uint64_t length; }; + +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 */ +}; + +struct fuse_iomap_upsert_out { + uint64_t nodeid; /* Inode ID */ + uint64_t attr_ino; /* matches fuse_attr:ino */ + + /* read file data from here */ + struct fuse_iomap_io read; + + /* write file data to here, if applicable */ + struct fuse_iomap_io write; +}; #endif /* _LINUX_FUSE_H */ diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index b7a099bea6921e..326c8f061aecfa 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -2173,6 +2173,47 @@ int fuse_lowlevel_iomap_device_remove(struct fuse_session *se, int device_id); int fuse_lowlevel_iomap_device_invalidate(struct fuse_session *se, int dev, off_t offset, off_t length); +/* + * 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 mapping information for file reads + * @param write mapping information for file writes + * @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_file_iomap *read, + const struct fuse_file_iomap *write); + +/** + * 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 nodeid the inode number + * @param attr_ino inode number as told by fuse_attr::ino + * @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, uint64_t attr_ino, + const struct fuse_iomap_inval *read, + const struct fuse_iomap_inval *write); + /* ----------------------------------------------------------- * * Utility functions * * ----------------------------------------------------------- */ diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index ee73de4a8950be..721abe2686d9c4 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -3504,6 +3504,79 @@ int fuse_lowlevel_iomap_device_invalidate(struct fuse_session *se, int dev, return send_notify_iov(se, FUSE_NOTIFY_IOMAP_DEV_INVAL, iov, 2); } +int fuse_lowlevel_notify_iomap_upsert(struct fuse_session *se, + fuse_ino_t nodeid, uint64_t attr_ino, + const struct fuse_file_iomap *read, + const struct fuse_file_iomap *write) +{ + struct fuse_iomap_upsert_out outarg = { + .nodeid = nodeid, + .attr_ino = attr_ino, + .read = { + .type = FUSE_IOMAP_TYPE_NOCACHE, + }, + .write = { + .type = FUSE_IOMAP_TYPE_NOCACHE, + } + }; + struct iovec iov[2]; + + if (!se) + return -EINVAL; + + if (se->conn.proto_minor < 99) + return -ENOSYS; + + if (!read && !write) + return 0; + + if (read) + fuse_iomap_to_kernel(&outarg.read, read); + + if (write) + fuse_iomap_to_kernel(&outarg.write, write); + + 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, uint64_t attr_ino, + const struct fuse_iomap_inval *read, + const struct fuse_iomap_inval *write) +{ + struct fuse_iomap_inval_out outarg = { + .nodeid = nodeid, + .attr_ino = attr_ino, + }; + struct iovec iov[2]; + + if (!se) + return -EINVAL; + + if (se->conn.proto_minor < 99) + 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 102f449d28a0be..a83966b9e48018 100644 --- a/lib/fuse_versionscript +++ b/lib/fuse_versionscript @@ -235,6 +235,8 @@ FUSE_3.99 { fuse_reply_iomap_config; fuse_lowlevel_iomap_device_invalidate; fuse_fs_iomap_device_invalidate; + fuse_lowlevel_notify_iomap_upsert; + fuse_lowlevel_notify_iomap_inval; } FUSE_3.18; # Local Variables: