[PATCH 16/18] libfuse: add low level code to invalidate iomap block device ranges

Darrick J. Wong
2025-09-16T00:45:57Z

Make it easier to invalidate the page cache for a block device that is
being used in conjunction with iomap. This allows a fuse server to kill
all cached data for a block that is being freed, so that block reuse
doesn't result in file corruption.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
include/fuse_kernel.h | 9 +++++++++
include/fuse_lowlevel.h | 15 +++++++++++++++
lib/fuse_lowlevel.c | 22 ++++++++++++++++++++++
lib/fuse_versionscript | 1 +
4 files changed, 47 insertions(+)

diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h
index 4009d0109711d9..861c3d449db788 100644
--- a/include/fuse_kernel.h
+++ b/include/fuse_kernel.h
@@ -240,6 +240,7 @@
* - add FUSE_IOMAP and iomap_{begin,end,ioend} for regular file operations
* - add FUSE_ATTR_IOMAP to enable iomap for specific inodes
* - add FUSE_IOMAP_CONFIG so the fuse server can configure more fs geometry
+ * - add FUSE_NOTIFY_IOMAP_DEV_INVAL to invalidate iomap bdev ranges
*/

#ifndef _LINUX_FUSE_H
@@ -689,6 +690,7 @@ enum fuse_notify_code {
FUSE_NOTIFY_DELETE = 6,
FUSE_NOTIFY_RESEND = 7,
FUSE_NOTIFY_INC_EPOCH = 8,
+ FUSE_NOTIFY_IOMAP_DEV_INVAL = 9,
FUSE_NOTIFY_CODE_MAX,
};

@@ -1394,4 +1396,11 @@ struct fuse_iomap_config_out {
int64_t s_maxbytes; /* max file size */
};

+struct fuse_iomap_dev_inval {
+ uint32_t dev; /* device cookie */
+ uint32_t reserved; /* zero */
+
+ uint64_t offset; /* range to invalidate pagecache, bytes */
+ uint64_t length;
+};
#endif /* _LINUX_FUSE_H */
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index b79c73d1573c40..687e14b8fea64f 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -2158,6 +2158,21 @@ int fuse_lowlevel_iomap_device_add(struct fuse_session *se, int fd,
*/
int fuse_lowlevel_iomap_device_remove(struct fuse_session *se, int device_id);

+/*
+ * Invalidate the page cache of a block device opened for use with iomap.
+ *
+ * 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 dev device cookie returned by fuse_lowlevel_iomap_add_device
+ * @param offset start of the range to invalidate, in bytes
+ * @return length length of the range to invalidate, in bytes
+ */
+int fuse_lowlevel_iomap_device_invalidate(struct fuse_session *se, int dev,
+ off_t offset, off_t length);
+
/* ----------------------------------------------------------- *
* Utility functions *
* ----------------------------------------------------------- */
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 26c23527439feb..166bd82eca463b 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -3483,6 +3483,28 @@ int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
return res;
}

+int fuse_lowlevel_iomap_device_invalidate(struct fuse_session *se, int dev,
+ off_t offset, off_t length)
+{
+ struct fuse_iomap_dev_inval arg = {
+ .dev = dev,
+ .offset = offset,
+ .length = length,
+ };
+ struct iovec iov[2];
+
+ if (!se)
+ return -EINVAL;
+
+ if (!(se->conn.want_ext & FUSE_CAP_IOMAP))
+ return -ENOSYS;
+
+ iov[1].iov_base = &arg;
+ iov[1].iov_len = sizeof(arg);
+
+ return send_notify_iov(se, FUSE_NOTIFY_IOMAP_DEV_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 6e57e943a60e2d..d268471ae5bd38 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -234,6 +234,7 @@ FUSE_3.99 {
fuse_fs_can_enable_iomapx;
fuse_lowlevel_discover_iomap;
fuse_reply_iomap_config;
+ fuse_lowlevel_iomap_device_invalidate;
} FUSE_3.18;